Skip to content

Feature/on chain settlement fen#671

Open
Abdullahi130 wants to merge 2 commits into
NOVUS-X:mainfrom
Abdullahi130:feature/on-chain-settlement-fen
Open

Feature/on chain settlement fen#671
Abdullahi130 wants to merge 2 commits into
NOVUS-X:mainfrom
Abdullahi130:feature/on-chain-settlement-fen

Conversation

@Abdullahi130

@Abdullahi130 Abdullahi130 commented Apr 24, 2026

Copy link
Copy Markdown

I have successfully implemented Contract: On-chain Puzzle Reward Verification,
I look forward to working with you next time,
Thank you.

closes #520

Summary by CodeRabbit

  • New Features

    • Game board state is now tracked per game with automatic settlement on completion.
    • Winner determination and payout are triggered when games reach final status.
  • Style

    • Wallet modal redesigned with teal glow effects and enhanced button styling.
    • Web3 status bar visually refreshed with improved indicators for connection status and active transactions.
  • Improvements

    • Enhanced blockchain contract interaction for more reliable operations.

@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR consolidates game-state management by removing the separate game_state.rs module and integrating board tracking directly into lib.rs with automatic game settlement based on status flags. Soroban contract invocation in the frontend wallet context is reimplemented using stellar-sdk. UI components receive visual styling refinements with glow effects and enhanced animations.

Changes

Cohort / File(s) Summary
Smart Contract State Consolidation
contracts/game_contract/src/game_state.rs, contracts/game_contract/src/lib.rs
Removed dedicated game_state.rs module and integrated board FEN tracking into Game struct; updated create_game to accept initial board state and submit_move to accept new board state and game status flag; auto-settlement logic added to settle games as Completed (status 1) with winner payout or Drawn (status 2) with draw payout.
Contract Test Updates
contracts/game_contract/src/test.rs
Updated test calls to pass new Bytes parameter for initial board state to align with revised create_game signature.
Frontend Wallet Integration
frontend/context/walletContext.tsx
Replaced invokeSorobanContract implementation from soroban-client with stellar-sdk-based invocation; builds transactions via contract.call(), runs simulation, signs via Freighter, submits to Soroban RPC, with explicit error handling.
Frontend Component Styling
frontend/components/WalletConnectModal.tsx, frontend/components/Web3StatusBar.tsx
Enhanced modal and status bar with teal/aqua glow effects, blurred gradient layers, ping animations for connected state, and richer layered styling with hover transitions and stronger shadows.

Sequence Diagram

sequenceDiagram
    participant Frontend as Frontend App
    participant Freighter as Freighter Wallet
    participant SorobanRPC as Soroban RPC
    participant Contract as Smart Contract

    Frontend->>Frontend: Build transaction with contract.call(args)
    Frontend->>SorobanRPC: simulateTransaction(tx)
    SorobanRPC->>Contract: Execute simulation
    Contract-->>SorobanRPC: Return transactionData
    SorobanRPC-->>Frontend: Return simulation result
    Frontend->>Freighter: signWithFreighter(xdr)
    Freighter-->>Frontend: Return signed tx
    Frontend->>SorobanRPC: sendTransaction(signedTx)
    SorobanRPC->>Contract: Submit transaction
    Contract-->>SorobanRPC: Return status
    SorobanRPC-->>Frontend: Return result or error
    alt Submission Error
        Frontend->>Frontend: throw error
    else Success
        Frontend->>Frontend: Return response
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 The board now lives in lib's embrace,
No separate state to chase!
Freighter signs with Stellar's dance,
While glowing buttons catch the glance—
Game settled swift, on-chain and bright,
Our contracts leap with stellar light! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive Title is vague and uses an abbreviated technical term without context; 'Feature/on chain settlement fen' lacks clarity about what 'fen' means and what the settlement accomplishes. Expand title to clearly describe the main change, e.g., 'Add FEN board state tracking and auto-settlement logic to game contract'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed PR implements on-chain game settlement with FEN board tracking, comprehensive test updates, and Soroban invocation wiring, addressing all coding objectives from issue #520.
Out of Scope Changes check ✅ Passed All changes directly support on-chain settlement: contract refactoring, FEN state tracking, auto-settlement logic, test alignment, and frontend Soroban integration are all in scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@drips-wave

drips-wave Bot commented Apr 24, 2026

Copy link
Copy Markdown

@Abdullahi130 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (5)
frontend/context/walletContext.tsx (2)

232-246: ⚠️ Potential issue | 🟠 Major

Fallback branch will also fail and masks the real error.

submitTransaction accepts a Transaction/FeeBumpTransaction, not a raw XDR string — the as unknown as Transaction cast will serialize the wrong payload and the fallback will throw anyway. Also, re-entering a second submitTransaction wipes out the original err (which likely contained Horizon's useful extras.result_codes). Drop the fallback and surface err directly.

     try {
       const txObj = TransactionBuilder.fromXDR(signedEnvelopeXDR, NETWORK_PASSPHRASE);
       const res = await server.submitTransaction(txObj);
       return res;
     } catch (err) {
-      // some horizon clients expect a TransactionEnvelope object; try submitting as-is
-      try {
-        // fallback: try submitting as-is (deprecated)
-        const res = await server.submitTransaction(signedEnvelopeXDR as unknown as Transaction);
-        return res;
-      } catch (e) {
-        console.error("submitTransaction failed", e);
-        throw e;
-      }
+      console.error("submitTransaction failed", err);
+      throw err;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/context/walletContext.tsx` around lines 232 - 246, The fallback
branch that casts signedEnvelopeXDR to Transaction and re-calls
server.submitTransaction should be removed because
TransactionBuilder.fromXDR/submitTransaction expect proper
Transaction/FeeBumpTransaction objects and the cast will fail and mask the
original error; in the catch for TransactionBuilder.fromXDR(signedEnvelopeXDR,
NETWORK_PASSPHRASE) simply log or re-throw the original err (preserving Horizon
extras/result_codes) — i.e., delete the inner try/catch that attempts
server.submitTransaction(signedEnvelopeXDR as unknown as Transaction) and
replace it with a direct processLogger/console.error of err and throw err.

165-165: ⚠️ Potential issue | 🔴 Critical

Bug: useless ternary always calls the same function.

Both branches of the ternary are identical, so the guard does nothing and will throw if freighter.getPublicKey is undefined on a given variant (e.g. freighter-api v2 which exposes requestAccess / getAddress).

-      const publicKey = await (freighter.getPublicKey ? freighter.getPublicKey() : freighter.getPublicKey());
+      let publicKey: string | undefined;
+      if (typeof freighter.getAddress === "function") {
+        const res = await freighter.getAddress();
+        publicKey = typeof res === "string" ? res : res?.address;
+      } else if (typeof freighter.getPublicKey === "function") {
+        publicKey = await freighter.getPublicKey();
+      } else if (typeof freighter.requestAccess === "function") {
+        publicKey = await freighter.requestAccess();
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/context/walletContext.tsx` at line 165, The ternary always calls
freighter.getPublicKey and will throw when that method doesn't exist; update the
publicKey acquisition to check for available methods and call the correct one:
if freighter.getPublicKey exists await freighter.getPublicKey(), else if
freighter.getAddress exists ensure access (await freighter.requestAccess() if
present) then await freighter.getAddress(), otherwise handle the absent API
(throw or return null). Replace the current line that sets publicKey with this
conditional sequence using the freighter methods (getPublicKey, requestAccess,
getAddress) and ensure the result is assigned to the publicKey variable.
contracts/game_contract/src/lib.rs (3)

178-236: ⚠️ Potential issue | 🟠 Major

Add bounds on initial_board size.

initial_board: Bytes is accepted verbatim into persistent instance storage with no length check. A caller can push a very large blob into Game.board_fen to inflate storage/CPU on every subsequent load of the GAMES map (every join_game, submit_move, payout, file_dispute, etc. deserializes the whole map). A standard FEN is ≤ ~90 bytes; cap it explicitly (e.g. <= 128) and reject oversized inputs up front.

     pub fn create_game(
         env: Env,
         player1: Address,
         wager_amount: i128,
         initial_board: Bytes,
     ) -> Result<u64, ContractError> {
+        if initial_board.len() > 128 {
+            return Err(ContractError::InvalidMove);
+        }
         let max_stake: i128 = env.storage().instance().get(&MAX_STAKE).unwrap_or(1_000);

Apply the same bound to new_board in submit_move. As per the issue's "efficient resource utilization (Gas/CPU)" acceptance criterion.

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

In `@contracts/game_contract/src/lib.rs` around lines 178 - 236, Add an explicit
size check (e.g. max 128 bytes) for board FEN data: in create_game, validate
initial_board.len() <= 128 before constructing/storing the Game (and return a
suitable ContractError such as BoardTooLarge) so oversized blobs are rejected up
front instead of being persisted to Game.board_fen; do the same validation in
submit_move for the new_board parameter before accepting/storing the move.
Ensure you reference the create_game and submit_move functions and the
Game.board_fen/new_board symbols when adding the checks and error return.

292-356: ⚠️ Potential issue | 🔴 Critical

Critical trust bug: player-supplied game_status lets any player claim the entire pot.

submit_move trusts the caller's game_status argument and, when it's 1, immediately marks the game Completed, sets winner = player, and calls process_payout(&env, &game, &player) — transferring both stakes (minus fee) to the submitter. Nothing on-chain verifies that the submitted move actually delivers checkmate, that the FEN is legal, or that the opponent agrees. A malicious player only needs to call submit_move(game_id, self, [anything_non_empty], any_bytes, 1) on their turn to drain the pot. game_status == 2 has the symmetric bug for draws.

This defeats the stated purpose of on-chain settlement (issue #520). At minimum the contract must not accept the outcome as an input. Options in order of preference:

  1. Verify the terminal condition on-chain from new_board (FEN parse + legal-move/mate/stalemate check).
  2. Require dual attestation — both players must co-sign a settlement payload (e.g. a separate settle_game(game_id, outcome, sig_p1, sig_p2) entrypoint), similar to the existing claim_puzzle_reward ED25519 flow.
  3. Require backend/oracle ED25519 signature over (game_id, final_fen, outcome, nonce) and verify in submit_move using the existing ADMIN_KEY infrastructure.

Until one of the above is in place, revert submit_move to only recording the move + board and keep settlement on the existing claim_draw / forfeit / arbitrator paths.

🛠️ Minimal interim patch — stop trusting the status flag
-    pub fn submit_move(
-        env: Env,
-        game_id: u64,
-        player: Address,
-        move_data: Vec<u32>,
-        new_board: Bytes,
-        game_status: u32,
-    ) -> Result<(), ContractError> {
+    pub fn submit_move(
+        env: Env,
+        game_id: u64,
+        player: Address,
+        move_data: Vec<u32>,
+        new_board: Bytes,
+    ) -> Result<(), ContractError> {
         ...
         game.board_fen = new_board;
-
-        // Auto settlement logic based on status flag
-        if game_status == 1 {
-            // Checkmate: submitting player wins
-            game.state = GameState::Completed;
-            game.winner = Some(player.clone());
-            Self::process_payout(&env, &game, &player)?;
-        } else if game_status == 2 {
-            // Draw
-            game.state = GameState::Drawn;
-            Self::process_draw_payout(&env, &game)?;
-        }
+        // Settlement must go through an authenticated path
+        // (dual-signed `settle_game`, oracle-signed verification, or on-chain FEN+mate check).
         ...
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/game_contract/src/lib.rs` around lines 292 - 356, submit_move
currently trusts the caller-provided game_status and immediately finalizes and
pays out via process_payout/process_draw_payout, allowing malicious players to
steal the pot; remove that trust by deleting the auto-settlement branch in
submit_move (the if game_status == 1 / else if game_status == 2 block), do not
set game.state to Completed/Drawn, do not set game.winner, and do not call
process_payout or process_draw_payout from submit_move — instead only record the
move (ChessMove), update board_fen, last_move_at and current_turn as you already
do and leave finalization to existing settlement flows (claim_draw, forfeit,
arbitrator) or implement one of the recommended secure options (on-chain FEN
verification, dual-attestation settle_game entrypoint, or oracle signature
verification using ADMIN_KEY) in a separate entrypoint.

1905-1972: ⚠️ Potential issue | 🟠 Major

Tests exercise the new signature but do not cover auto-settlement.

The new submit_move(..., new_board, game_status) has no test that passes game_status = 1 or 2 and verifies the winner/draw payout side-effect (or — once fixed per the critical comment above — that a bogus status is rejected). Given this is the entire point of the PR and the linked issue's acceptance criterion ("Unit tests cover standard and edge cases"), please add coverage for:

  • checkmate auto-settlement transfers the pot to the submitter,
  • draw auto-settlement returns stakes to both players,
  • an unauthorized/unverified status value is rejected.

Want me to draft these tests once the settlement path is redesigned?

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

In `@contracts/game_contract/src/lib.rs` around lines 1905 - 1972, Add unit tests
exercising the new submit_move signature to cover auto-settlement and invalid
statuses: create a game via create_game/join_game, fund players with
StellarAssetClient::mint, initialize token with client.initialize_token and
set_max_stake, then call submit_move with game_status = 1 and assert the
submitter receives the pot (check token balances and game state via get_game),
call submit_move with game_status = 2 and assert stakes are refunded to both
players (check balances and game.moves/game.current_turn), and call
try_submit_move with an invalid/unauthorized status and assert it returns an
Err(Ok(...)) matching the contract error for invalid game status (mirror how
NotYourTurn/InvalidMove are asserted already).
🧹 Nitpick comments (3)
contracts/game_contract/src/lib.rs (2)

292-338: game_status is undocumented and uses magic numbers.

Even after the settlement-trust issue above is addressed, the raw u32 with inline comments (1 = checkmate, 2 = draw) is error-prone. Use a #[contracttype] enum MoveOutcome { Ongoing, Checkmate, Draw, ... } so the ABI is self-describing and invalid values are rejected at decode time.


1204-1206: Two #[cfg(test)] mods in the same file — confirm intentional split.

Line 1205 declares mod test; (external test.rs) and immediately below, line 1208 defines mod tests { ... } inline in this file. Both names compile, but it makes test discovery confusing (cargo test runs both, with overlapping setup helpers). Consider moving the inline tests block into test.rs as a submodule, or rename one (e.g. mod puzzle_tests) to clarify intent.

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

In `@contracts/game_contract/src/lib.rs` around lines 1204 - 1206, There are two
cfg(test) modules declared — the external module `mod test;` and the inline `mod
tests { ... }` — causing duplicate/overlapping test discovery; either move the
inline `mod tests { ... }` contents into the external `test.rs` as a submodule
under `test` or rename the inline module (e.g., `mod puzzle_tests`) and update
any references to its helper functions, ensuring only one intentionally named
test module provides shared setup helpers (`mod test` vs `mod tests`) to avoid
confusion during `cargo test`.
frontend/context/walletContext.tsx (1)

249-300: Input validation: contractId, functionName, args are passed directly to Contract.call without type coercion.

Contract.call(name, ...args) requires ScVals for args. If callers pass plain JS strings/numbers/Address objects (as in create_game(player1, wager_amount, initial_board)), the call will fail at build time or silently send incorrect data. Consider either (a) documenting that callers must supply xdr.ScVals, or (b) accepting typed inputs here and converting via nativeToScVal before contract.call.

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

In `@frontend/context/walletContext.tsx` around lines 249 - 300,
invokeSorobanContract currently forwards contractId, functionName, and args
directly into Contract.call which requires xdr.ScVal arguments; convert and
validate inputs before calling: ensure contractId and functionName are strings
(throw if not), map each entry of args through stellar-sdk's nativeToScVal (and
handle Address/BigInt/number/boolean/null/array/object cases) to produce ScVal
instances, then call contract.call(functionName, ...convertedArgs); import
nativeToScVal where needed and add clear error messages for invalid arg types so
callers don't send plain JS values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/components/WalletConnectModal.tsx`:
- Around line 113-116: In WalletConnectModal, the glowing aura div is inside
Dialog.Content with z-[-1], so it's rendered behind the dialog's semi-opaque
background and becomes invisible; move the aura out of Dialog.Content and place
it as a sibling (e.g., directly under the Dialog.Portal or the Dialog wrapper)
so it lives in the same portal but outside the dialog's painted background, keep
its positioning classes (absolute, -inset-1, rounded-2xl, bg-gradient-to-r,
blur-xl, opacity-70) and adjust stacking (give the aura a lower z-index than the
dialog container but not inside the dialog itself) so the glow is visible around
the modal edges; alternatively, if you prefer CSS, implement the aura as a
::before pseudo-element on the dialog wrapper element (e.g., the element
rendered by Dialog.Content) positioned outside the clipped rounded container so
the gradient shows through the edges.

In `@frontend/context/walletContext.tsx`:
- Line 281: The current call to TransactionBuilder.assembleTransaction(tx,
NETWORK_PASSPHRASE, simulatedTx) is incompatible with stellar-sdk v10.4.0;
replace the static call and wrong signature by importing SorobanRpc from
stellar-sdk and calling SorobanRpc.assembleTransaction(tx, simulatedTx) (then
.build())—remove NETWORK_PASSPHRASE and stop using
TransactionBuilder.assembleTransaction; update the import and the call site
where tx and simulatedTx are used.
- Around line 289-293: Replace the hardcoded string comparison and add polling
for a terminal transaction state: use the SDK enum
SorobanRpc.Api.SendTransactionStatus instead of the literal "ERROR" when
checking sendResponse.status (refer to sendTransaction and sendResponse), and
after receiving sendResponse.hash call
rpcServer.getTransaction(sendResponse.hash) in a short retry loop that handles
NOT_FOUND and PENDING states until the status becomes a terminal state (e.g.,
SUCCESS or FAILED per the SDK enum); if the final status is an error, throw with
the error details (use sendResponse.errorResult or the final getTransaction
result), otherwise return the final successful result to the caller.

---

Outside diff comments:
In `@contracts/game_contract/src/lib.rs`:
- Around line 178-236: Add an explicit size check (e.g. max 128 bytes) for board
FEN data: in create_game, validate initial_board.len() <= 128 before
constructing/storing the Game (and return a suitable ContractError such as
BoardTooLarge) so oversized blobs are rejected up front instead of being
persisted to Game.board_fen; do the same validation in submit_move for the
new_board parameter before accepting/storing the move. Ensure you reference the
create_game and submit_move functions and the Game.board_fen/new_board symbols
when adding the checks and error return.
- Around line 292-356: submit_move currently trusts the caller-provided
game_status and immediately finalizes and pays out via
process_payout/process_draw_payout, allowing malicious players to steal the pot;
remove that trust by deleting the auto-settlement branch in submit_move (the if
game_status == 1 / else if game_status == 2 block), do not set game.state to
Completed/Drawn, do not set game.winner, and do not call process_payout or
process_draw_payout from submit_move — instead only record the move (ChessMove),
update board_fen, last_move_at and current_turn as you already do and leave
finalization to existing settlement flows (claim_draw, forfeit, arbitrator) or
implement one of the recommended secure options (on-chain FEN verification,
dual-attestation settle_game entrypoint, or oracle signature verification using
ADMIN_KEY) in a separate entrypoint.
- Around line 1905-1972: Add unit tests exercising the new submit_move signature
to cover auto-settlement and invalid statuses: create a game via
create_game/join_game, fund players with StellarAssetClient::mint, initialize
token with client.initialize_token and set_max_stake, then call submit_move with
game_status = 1 and assert the submitter receives the pot (check token balances
and game state via get_game), call submit_move with game_status = 2 and assert
stakes are refunded to both players (check balances and
game.moves/game.current_turn), and call try_submit_move with an
invalid/unauthorized status and assert it returns an Err(Ok(...)) matching the
contract error for invalid game status (mirror how NotYourTurn/InvalidMove are
asserted already).

In `@frontend/context/walletContext.tsx`:
- Around line 232-246: The fallback branch that casts signedEnvelopeXDR to
Transaction and re-calls server.submitTransaction should be removed because
TransactionBuilder.fromXDR/submitTransaction expect proper
Transaction/FeeBumpTransaction objects and the cast will fail and mask the
original error; in the catch for TransactionBuilder.fromXDR(signedEnvelopeXDR,
NETWORK_PASSPHRASE) simply log or re-throw the original err (preserving Horizon
extras/result_codes) — i.e., delete the inner try/catch that attempts
server.submitTransaction(signedEnvelopeXDR as unknown as Transaction) and
replace it with a direct processLogger/console.error of err and throw err.
- Line 165: The ternary always calls freighter.getPublicKey and will throw when
that method doesn't exist; update the publicKey acquisition to check for
available methods and call the correct one: if freighter.getPublicKey exists
await freighter.getPublicKey(), else if freighter.getAddress exists ensure
access (await freighter.requestAccess() if present) then await
freighter.getAddress(), otherwise handle the absent API (throw or return null).
Replace the current line that sets publicKey with this conditional sequence
using the freighter methods (getPublicKey, requestAccess, getAddress) and ensure
the result is assigned to the publicKey variable.

---

Nitpick comments:
In `@contracts/game_contract/src/lib.rs`:
- Around line 1204-1206: There are two cfg(test) modules declared — the external
module `mod test;` and the inline `mod tests { ... }` — causing
duplicate/overlapping test discovery; either move the inline `mod tests { ... }`
contents into the external `test.rs` as a submodule under `test` or rename the
inline module (e.g., `mod puzzle_tests`) and update any references to its helper
functions, ensuring only one intentionally named test module provides shared
setup helpers (`mod test` vs `mod tests`) to avoid confusion during `cargo
test`.

In `@frontend/context/walletContext.tsx`:
- Around line 249-300: invokeSorobanContract currently forwards contractId,
functionName, and args directly into Contract.call which requires xdr.ScVal
arguments; convert and validate inputs before calling: ensure contractId and
functionName are strings (throw if not), map each entry of args through
stellar-sdk's nativeToScVal (and handle
Address/BigInt/number/boolean/null/array/object cases) to produce ScVal
instances, then call contract.call(functionName, ...convertedArgs); import
nativeToScVal where needed and add clear error messages for invalid arg types so
callers don't send plain JS values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e525ee8a-bb6b-4898-a3b3-64fcaf2ef4e3

📥 Commits

Reviewing files that changed from the base of the PR and between 33aac9a and 35acd99.

📒 Files selected for processing (6)
  • contracts/game_contract/src/game_state.rs
  • contracts/game_contract/src/lib.rs
  • contracts/game_contract/src/test.rs
  • frontend/components/WalletConnectModal.tsx
  • frontend/components/Web3StatusBar.tsx
  • frontend/context/walletContext.tsx
💤 Files with no reviewable changes (1)
  • contracts/game_contract/src/game_state.rs

Comment on lines +113 to +116
<Dialog.Content className="fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-gray-600/50 bg-gray-900/80 backdrop-blur-2xl p-0 shadow-[0_0_50px_-12px_rgba(20,184,166,0.3)] animate-modal-in focus:outline-none">
{/* Glowing Aura Background */}
<div className="absolute -inset-1 rounded-2xl bg-gradient-to-r from-teal-500/20 to-blue-600/20 blur-xl z-[-1] opacity-70 pointer-events-none"></div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Glowing aura will likely be invisible behind the modal's own background.

The aura is absolute z-[-1] inside a Dialog.Content that itself has bg-gray-900/80 backdrop-blur-2xl. Negative z within the same stacking context puts the aura behind the parent's painted background, so the blurred gradient won't show. Options: move the aura outside Dialog.Content (as a sibling in the portal) or use a ::before pseudo-element with the aura positioned outside the clipped rounded container so it glows through the edges.

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

In `@frontend/components/WalletConnectModal.tsx` around lines 113 - 116, In
WalletConnectModal, the glowing aura div is inside Dialog.Content with z-[-1],
so it's rendered behind the dialog's semi-opaque background and becomes
invisible; move the aura out of Dialog.Content and place it as a sibling (e.g.,
directly under the Dialog.Portal or the Dialog wrapper) so it lives in the same
portal but outside the dialog's painted background, keep its positioning classes
(absolute, -inset-1, rounded-2xl, bg-gradient-to-r, blur-xl, opacity-70) and
adjust stacking (give the aura a lower z-index than the dialog container but not
inside the dialog itself) so the glow is visible around the modal edges;
alternatively, if you prefer CSS, implement the aura as a ::before
pseudo-element on the dialog wrapper element (e.g., the element rendered by
Dialog.Content) positioned outside the clipped rounded container so the gradient
shows through the edges.

throw new Error("Simulation failed: missing transactionData");
}

tx = TransactionBuilder.assembleTransaction(tx, NETWORK_PASSPHRASE, simulatedTx as any).build();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

stellar-sdk SorobanRpc assembleTransaction signature arguments

💡 Result:

In the @stellar/stellar-sdk JavaScript library, assembleTransaction is a static function exported from the RPC module (lib/rpc/transaction.js). It combines a raw Soroban transaction with simulation results from the Soroban RPC simulateTransaction method to produce a TransactionBuilder with applied authorization data, resources (fee, footprint), and Soroban transaction data. Signature: assembleTransaction(raw, simulation) → TransactionBuilder Arguments: - raw: Transaction or FeeBumpTransaction - the initial transaction without simulation applied (must contain exactly one invokeHostFunction, extendFootprintTtl, or restoreFootprint operation). - simulation: Api.SimulateTransactionResponse or Api.RawSimulateTransactionResponse - the Soroban RPC simulation result. The function: - Handles FeeBumpTransactions by recursing to the inner transaction. - Validates the transaction is Soroban-compatible and simulation succeeded. - Adjusts fees to sum classic fee and simulation minResourceFee (avoiding double-counting if raw already has SorobanData). - Clones the raw transaction via TransactionBuilder.cloneFrom, applying simulation SorobanData. - For invokeHostFunction ops, replaces auth with simulation auth (unless raw already has auth, then keeps existing). This is typically used internally by higher-level methods like Server.prepareTransaction (which simulates then assembles) or AssembledTransaction flows, but can be called directly for advanced control. Example usage pattern (from SDK patterns): const simulation = await server.simulateTransaction(rawTx); const assembledBuilder = rpc.assembleTransaction(rawTx, simulation); // Then sign: assembledBuilder.sign(keypair); const tx = assembledBuilder.build; Note: Soroban RPC server itself has no assembleTransaction method; standard RPC methods are simulateTransaction, sendTransaction, etc. The function is SDK-side. Official source: @stellar/stellar-sdk documentation.

Citations:


🏁 Script executed:

# Check the imports and package information
cd frontend/context && head -50 walletContext.tsx | grep -E "^import|^from"

Repository: NOVUS-X/XLMate

Length of output: 40


🏁 Script executed:

# Find package.json to check stellar-sdk version
fd package.json | head -5 | xargs grep -l "stellar-sdk" 2>/dev/null

Repository: NOVUS-X/XLMate

Length of output: 79


🏁 Script executed:

# Search for other uses of assembleTransaction in the codebase
rg "assembleTransaction" -A 2 -B 2

Repository: NOVUS-X/XLMate

Length of output: 380


🏁 Script executed:

head -100 frontend/context/walletContext.tsx | cat -n

Repository: NOVUS-X/XLMate

Length of output: 3973


🏁 Script executed:

cat frontend/package.json | grep -A 2 -B 2 "stellar"

Repository: NOVUS-X/XLMate

Length of output: 392


🏁 Script executed:

sed -n '260,290p' frontend/context/walletContext.tsx | cat -n

Repository: NOVUS-X/XLMate

Length of output: 1448


🏁 Script executed:

rg "import.*TransactionBuilder" frontend/context/walletContext.tsx

Repository: NOVUS-X/XLMate

Length of output: 143


🏁 Script executed:

rg "import.*SorobanRpc\|import.*rpc" frontend/context/walletContext.tsx

Repository: NOVUS-X/XLMate

Length of output: 40


🏁 Script executed:

head -50 frontend/context/walletContext.tsx | cat -n | grep -v "^[[:space:]]*[0-9]\+[[:space:]]*\/\/"

Repository: NOVUS-X/XLMate

Length of output: 97


🏁 Script executed:

rg "rpcServer\s*=" frontend/context/walletContext.tsx | head -5

Repository: NOVUS-X/XLMate

Length of output: 127


🏁 Script executed:

rg "import.*Server" frontend/context/walletContext.tsx

Repository: NOVUS-X/XLMate

Length of output: 143


assembleTransaction call is incompatible with stellar-sdk 10.4.0 API.

The method signature is assembleTransaction(raw, simulation) with 2 arguments only. The NETWORK_PASSPHRASE parameter is incorrect and the method is not available as a static on TransactionBuilder. Import SorobanRpc from stellar-sdk and call:

tx = SorobanRpc.assembleTransaction(tx, simulatedTx).build();

This will fail at runtime with the current implementation.

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

In `@frontend/context/walletContext.tsx` at line 281, The current call to
TransactionBuilder.assembleTransaction(tx, NETWORK_PASSPHRASE, simulatedTx) is
incompatible with stellar-sdk v10.4.0; replace the static call and wrong
signature by importing SorobanRpc from stellar-sdk and calling
SorobanRpc.assembleTransaction(tx, simulatedTx) (then .build())—remove
NETWORK_PASSPHRASE and stop using TransactionBuilder.assembleTransaction; update
the import and the call site where tx and simulatedTx are used.

Comment on lines +289 to +293
const sendResponse = await rpcServer.sendTransaction(signedTx);

if (sendResponse.status === "ERROR") {
throw new Error(`Transaction failed: ${JSON.stringify(sendResponse.errorResult)}`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

stellar-sdk SorobanRpc sendTransaction getTransaction poll status PENDING SUCCESS

💡 Result:

To use Stellar SDK's SorobanRpc to send a transaction, poll its status until PENDING, SUCCESS, or other final states: 1. Build, prepare, and sign your transaction using TransactionBuilder, server.prepareTransaction(tx), and tx.sign(keypair). 2. Call server.sendTransaction(signedTx) which returns a SendTransactionResponse with status typically "PENDING" and transaction hash. 3. Poll using server.getTransaction(hash) repeatedly until status changes from "NOT_FOUND" or "PENDING". Key statuses: - PENDING: Enqueued but not processed. - SUCCESS: Applied successfully (includes ledger, resultXdr, etc.). - FAILED: Failed (includes error details). - NOT_FOUND: Not yet visible or expired. - ERROR: Immediate rejection. Example polling loop (from SDK guides and examples): async function submitAndWait(server, tx) { const sendResult = await server.sendTransaction(tx); if (sendResult.status !== 'PENDING') { throw new Error(Send failed: ${sendResult.status}); } let txResult; while (true) { txResult = await server.getTransaction(sendResult.hash); if (txResult.status !== 'NOT_FOUND') { break; } await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1s } if (txResult.status === 'SUCCESS') { return txResult; } else { throw new Error(Transaction ${txResult.status}: ${txResult.errorResultXdr || ''}); } } Newer @stellar/stellar-sdk provides server.pollTransaction(hash, {sleepStrategy: => 500, attempts: 5}) for built-in polling. Status enum: SorobanRpc.Api.GetTransactionStatus.PENDING, .SUCCESS, .FAILED, .NOT_FOUND, .ERROR.

Citations:


🏁 Script executed:

cat -n frontend/context/walletContext.tsx | sed -n '270,310p'

Repository: NOVUS-X/XLMate

Length of output: 1634


🏁 Script executed:

head -50 frontend/context/walletContext.tsx

Repository: NOVUS-X/XLMate

Length of output: 1629


🏁 Script executed:

rg "^import|^from" frontend/context/walletContext.tsx | head -30

Repository: NOVUS-X/XLMate

Length of output: 265


🏁 Script executed:

rg "rpcServer" frontend/context/walletContext.tsx | head -20

Repository: NOVUS-X/XLMate

Length of output: 320


🏁 Script executed:

rg -B 5 "const invokeSorobanContract" frontend/context/walletContext.tsx | head -20

Repository: NOVUS-X/XLMate

Length of output: 198


🏁 Script executed:

rg "invokeSorobanContract" frontend/ -A 2 -B 1 | head -40

Repository: NOVUS-X/XLMate

Length of output: 1134


🏁 Script executed:

rg "SendTransactionResponse|GetTransactionStatus|SendTransactionStatus" frontend/ -A 3 | head -40

Repository: NOVUS-X/XLMate

Length of output: 40


🏁 Script executed:

rg "stellar-sdk" package.json

Repository: NOVUS-X/XLMate

Length of output: 40


🏁 Script executed:

fd package.json | head -5

Repository: NOVUS-X/XLMate

Length of output: 92


🏁 Script executed:

cat frontend/package.json | grep -A 2 -B 2 "stellar-sdk"

Repository: NOVUS-X/XLMate

Length of output: 213


🏁 Script executed:

cat package.json | grep -A 2 -B 2 "stellar-sdk"

Repository: NOVUS-X/XLMate

Length of output: 40


🏁 Script executed:

web_search

Repository: NOVUS-X/XLMate

Length of output: 108


🌐 Web query:

stellar-sdk 10.4.0 SendTransactionResponse status enum ERROR PENDING

💡 Result:

In stellar-sdk version 10.4.0, the SendTransactionResponse from server.sendTransaction (Soroban RPC method) has a 'status' field with possible enum values: PENDING, DUPLICATE, TRY_AGAIN_LATER, and ERROR. PENDING indicates successful enqueuing (poll getTransaction for completion). ERROR includes errorResultXdr and possibly diagnosticEventsXdr with rejection details. DUPLICATE and TRY_AGAIN_LATER handle retries/network issues. These match official Stellar Soroban RPC docs and SDK source code (parseRawSendTransaction). Version 10.4.0 (confirmed in CHANGELOG) supports this via js-stellar-sdk PRs adding RPC response parsing.

Citations:


Use the SDK enum for status comparison and poll for terminal transaction state before returning.

The code compares against the bare string "ERROR" instead of using SorobanRpc.Api.SendTransactionStatus.ERROR from the SDK. More critically, sendTransaction() returns PENDING when the transaction is enqueued but not yet applied—the function returns immediately (line 295) without polling getTransaction(hash) until reaching a terminal status (SUCCESS, FAILED, etc.). This means callers of invokeSorobanContract will incorrectly believe the transaction succeeded when it's only enqueued. You must poll getTransaction(sendResponse.hash) until the status changes from PENDING or NOT_FOUND before returning to the caller.

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

In `@frontend/context/walletContext.tsx` around lines 289 - 293, Replace the
hardcoded string comparison and add polling for a terminal transaction state:
use the SDK enum SorobanRpc.Api.SendTransactionStatus instead of the literal
"ERROR" when checking sendResponse.status (refer to sendTransaction and
sendResponse), and after receiving sendResponse.hash call
rpcServer.getTransaction(sendResponse.hash) in a short retry loop that handles
NOT_FOUND and PENDING states until the status becomes a terminal state (e.g.,
SUCCESS or FAILED per the SDK enum); if the final status is an error, throw with
the error details (use sendResponse.errorResult or the final getTransaction
result), otherwise return the final successful result to the caller.

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.

Contract: On-chain Puzzle Reward Verification

1 participant