Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions frontend/components/WalletConnectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,12 @@ export function WalletConnectModal({
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/60 backdrop-blur-sm animate-overlay-in" />

{/* Content */}
<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-700/80 bg-gray-900/95 backdrop-blur-xl p-0 shadow-2xl animate-modal-in focus:outline-none">
<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>

{/* Header gradient bar */}
<div className="h-1 w-full rounded-t-2xl bg-gradient-to-r from-teal-500 via-blue-600 to-indigo-500" />
<div className="h-1.5 w-full rounded-t-2xl bg-gradient-to-r from-teal-400 via-blue-500 to-indigo-500 shadow-[0_2px_10px_rgba(45,212,191,0.5)]" />

<div className="p-6 space-y-5">
{/* Close button */}
Expand Down Expand Up @@ -290,10 +293,10 @@ export function WalletConnectModal({
<button
onClick={handleConnect}
disabled={isConnecting}
className="w-full group relative py-3 rounded-xl font-semibold text-white overflow-hidden transition-all duration-300 hover:scale-[1.02] active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full group relative py-3.5 rounded-xl font-bold text-white overflow-hidden transition-all duration-300 hover:scale-[1.03] active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed shadow-[0_0_20px_rgba(20,184,166,0.15)] hover:shadow-[0_0_30px_rgba(20,184,166,0.4)]"
>
<div className="absolute inset-0 bg-gradient-to-r from-teal-500 to-blue-700 transition-all duration-300 group-hover:from-teal-600 group-hover:to-blue-800" />
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-gradient-to-r from-teal-400/20 to-blue-600/20" />
<div className="absolute inset-0 bg-gradient-to-r from-teal-500 via-blue-600 to-indigo-600 transition-all duration-300 group-hover:bg-gradient-to-l group-hover:scale-105" />
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.2)_0%,transparent_100%)]" />
<span className="relative z-10 flex items-center justify-center gap-2">
{isConnecting ? (
<>
Expand Down
29 changes: 17 additions & 12 deletions frontend/components/Web3StatusBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,33 @@ export function Web3StatusBar() {
`${addr.slice(0, 6)}...${addr.slice(-4)}`;

return (
<div className="flex items-center gap-3 px-4 py-2 rounded-xl bg-gray-800/40 border border-gray-700/30 text-sm" role="status" aria-label="Wallet connection status">
<span className={`w-2 h-2 rounded-full ${cfg.dot}`} aria-hidden="true" />
<span className={`${cfg.color} font-medium`}>{cfg.label}</span>
<div className="group relative flex items-center gap-3 px-5 py-2.5 rounded-full bg-gray-900/60 backdrop-blur-md border border-gray-700/50 shadow-lg hover:shadow-xl hover:border-gray-600/50 transition-all duration-300 text-sm cursor-default" role="status" aria-label="Wallet connection status">
<div className="absolute inset-0 rounded-full bg-gradient-to-r from-teal-500/10 to-blue-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />

<span className="relative flex h-2.5 w-2.5 items-center justify-center">
{status === "connected" && <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-75"></span>}
<span className={`relative inline-flex h-2.5 w-2.5 rounded-full ${cfg.dot}`} aria-hidden="true" />
</span>
<span className={`relative ${cfg.color} font-semibold tracking-wide drop-shadow-sm`}>{cfg.label}</span>
{address && (
<>
<span className="text-gray-600">|</span>
<span className="font-mono text-gray-300 text-xs">
<span className="relative text-gray-600">|</span>
<span className="relative font-mono text-gray-300 text-xs font-medium bg-gray-800/50 px-2 py-0.5 rounded-md border border-gray-700/30 shadow-inner">
{truncateAddress(address)}
</span>
</>
)}
<span className="text-gray-600">|</span>
<span className="text-xs text-gray-500 flex items-center gap-1">
<span className="w-1.5 h-1.5 rounded-full bg-yellow-500" aria-hidden="true" />
<span className="relative text-gray-600">|</span>
<span className="relative text-xs text-gray-400 font-medium flex items-center gap-1.5 bg-yellow-500/10 text-yellow-200/90 px-2.5 py-0.5 rounded-md border border-yellow-500/20 transition-all hover:bg-yellow-500/20">
<span className="w-1.5 h-1.5 rounded-full bg-yellow-500 shadow-[0_0_8px_rgba(234,179,8,0.8)]" aria-hidden="true" />
Testnet
</span>
{activeCount > 0 && (
<>
<span className="text-gray-600">|</span>
<span className="text-xs text-blue-400 flex items-center gap-1">
<span className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse" aria-hidden="true" />
{activeCount} tx
<span className="relative text-gray-600">|</span>
<span className="relative text-xs flex items-center gap-1.5 bg-blue-500/10 text-blue-300 px-2.5 py-0.5 rounded-md border border-blue-500/20 shadow-[0_0_10px_rgba(59,130,246,0.15)] transition-all">
<span className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse shadow-[0_0_8px_rgba(96,165,250,0.8)]" aria-hidden="true" />
{activeCount} tx active
</span>
</>
)}
Expand Down
56 changes: 44 additions & 12 deletions frontend/context/walletContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,19 +248,51 @@ export function AppProvider({ children }: { children: React.ReactNode }) {

const invokeSorobanContract = async (contractId: string, functionName: string, args: any[] = []) => {
if (!address) throw new Error("No wallet connected");
// Best-effort Soroban invocation using `soroban-client` if present. This is a helper that
// will build a transaction targeting the Soroban network and request Freighter to sign it.
// Actual invocation details (host function, footprints, etc.) depend on the contract ABI.
try {
// dynamic import to avoid build-time hard dependency when not used
const sc = await import("soroban-client");
const SorobanClient = sc.default;
// Modern Soroban SDK usage
const rpc = new SorobanClient(SOROBAN_RPC);
// TODO: Implement full hostfunction builder for specific contract/ABI
// Example: await rpc.getContractData(contractId);
// TODO: Implement full hostfunction builder for specific contract/ABI
throw new Error("invokeSorobanContract: implement contract-specific invocation (ABI required)");
// Dynamic import to avoid build-time issues
const { Contract, TimeoutInfinite } = await import("stellar-sdk");

// Use the Soroban RPC
const rpcServer = new Server(SOROBAN_RPC, { allowHttp: true });
const acct = await rpcServer.getAccount(address);
const contract = new Contract(contractId);

// Build the initial transaction
let tx = new TransactionBuilder(acct, {
fee: "10000",
networkPassphrase: NETWORK_PASSPHRASE,
})
.addOperation(contract.call(functionName, ...args))
.setTimeout(TimeoutInfinite)
.build();

// Simulate transaction to get correct footprints & fee
const simulatedTx = await rpcServer.simulateTransaction(tx);

if ("error" in simulatedTx && simulatedTx.error) {
throw new Error(`Simulation failed: ${simulatedTx.error}`);
}

// Assemble transaction with simulation data
if (!simulatedTx.transactionData) {
throw new Error("Simulation failed: missing transactionData");
}

tx = TransactionBuilder.assembleTransaction(tx, NETWORK_PASSPHRASE, simulatedTx as any).build();
Comment on lines 249 to +281

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

🏁 Script executed:

#!/bin/bash
fd -t f 'package.json' --exclude node_modules | xargs rg -n '"(`@stellar/`)?stellar-sdk"\s*:\s*"[^"]+"'

Repository: NOVUS-X/XLMate

Length of output: 111


🏁 Script executed:

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

Repository: NOVUS-X/XLMate

Length of output: 2324


🏁 Script executed:

rg -n "import.*stellar-sdk|from.*stellar-sdk" frontend/context/walletContext.tsx

Repository: NOVUS-X/XLMate

Length of output: 271


🏁 Script executed:

rg -n "invokeSorobanContract" frontend/context/walletContext.tsx -A 40

Repository: NOVUS-X/XLMate

Length of output: 4378


🏁 Script executed:

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

Repository: NOVUS-X/XLMate

Length of output: 719


🌐 Web query:

stellar-sdk 10.4.0 contract.call xdr.ScVal parameters

💡 Result:

In stellar-sdk version 10.4.0 (specifically @stellar/stellar-sdk), the Contract.call method is used to invoke a Soroban smart contract function and returns an xdr.Operation for InvokeHostFunctionOp. Method signature: contract.call(method: string, ...params: xdr.ScVal[]): xdr.Operation Parameters: - method: string - Name of the contract method to call. - ...params: xdr.ScVal[] - Variable number of xdr.ScVal arguments to pass to the function. Each parameter must be a properly constructed xdr.ScVal instance matching the contract function's expected input types. Usage example: const contract = new Contract("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE"); const op = contract.call("increment", xdr.ScVal.scvU32(5)); The SDK provides nativeToScVal and related utilities (via Contract.Spec) to convert JavaScript native values (numbers, strings, objects, arrays, etc.) to xdr.ScVal instances. For example: - Numbers: nativeToScVal(123, {type: 'u32'}) → xdr.ScVal.scvU32(123) - Addresses: Address.fromString("G...").toScVal - Strings: nativeToScVal("hello") → xdr.ScVal.scvString("hello") - Maps/Vecs: Automatically converted from objects/arrays with optional type hints. This API has remained stable across versions including 10.4.0, as confirmed by the official documentation and changelog. For full conversion details, see the scval.js source which handles type inference and validation.

Citations:


🌐 Web query:

stellar-sdk 10.4.0 TransactionBuilder.assembleTransaction signature networkPassphrase

💡 Result:

In @stellar/stellar-sdk version 10.4.0, TransactionBuilder does not have a direct method called assembleTransaction. The assembleTransaction function is a static utility in the rpc module that takes a raw Transaction or FeeBumpTransaction and a simulation response (Api.SimulateTransactionResponse or Api.RawSimulateTransactionResponse), returning a new TransactionBuilder with applied Soroban auth, resources, and adjusted fee. The networkPassphrase is preserved from the input raw transaction via TransactionBuilder.cloneFrom(raw, {..., networkPassphrase: raw.networkPassphrase}). Signature: assembleTransaction(raw: Transaction | FeeBumpTransaction, simulation: Api.SimulateTransactionResponse | Api.RawSimulateTransactionResponse): TransactionBuilder Usage example from source: const txnBuilder = TransactionBuilder.cloneFrom(raw, { fee: classicFeeNum.toString, sorobanData: success.transactionData.build, networkPassphrase: raw.networkPassphrase }); This is used in Soroban transaction preparation after simulateTransaction, typically via SorobanServer.prepareTransaction which internally calls it. The networkPassphrase ensures signatures are valid for the correct network (e.g., "Public Global Stellar Network ; September 2015" for mainnet).

Citations:


🌐 Web query:

stellar-sdk 10.4.0 SorobanRpc.Api.isSimulationError

💡 Result:

SorobanRpc.Api.isSimulationError is a static method in the @stellar/stellar-sdk JavaScript library (available in version 10.4.0 and later) that checks if a Soroban RPC simulateTransaction response indicates an error. It returns true if the simulation failed (e.g., the response has an 'error' field), and false otherwise. This method is used in typical Soroban transaction workflows to validate simulation results before assembling and submitting transactions. For example: if (SorobanRpc.Api.isSimulationError(sim)) { throw sim.error; // or handle the error } It pairs with related methods like SorobanRpc.Api.isSimulationSuccess (checks for successful simulation) and SorobanRpc.Api.isSimulationRestore (checks if footprint restoration is needed). The method is part of the rpc.Api namespace, imported as: import { SorobanRpc } from '@stellar/stellar-sdk'; Note: In newer SDK versions (post-v12), the import path may be '@stellar/stellar-sdk/rpc' for the Api, but the functionality remains the same. Version 10.4.0 specifically includes Soroban RPC support via updates to stellar-base.

Citations:


🌐 Web query:

stellar-sdk 10.4.0 SorobanRpc namespace import rpc module

💡 Result:

In stellar-sdk version 10.4.0, the Soroban RPC functionality is accessed via the SorobanRpc namespace. To import the rpc module (which provides the Soroban RPC Server, API types, and helpers like assembleTransaction), use: import { SorobanRpc } from 'stellar-sdk'; Then destructure as needed: const { Server, Api, assembleTransaction } = SorobanRpc; Example usage: const server = new SorobanRpc.Server('https://soroban-testnet.stellar.org'); This structure was established around v11.0.0 but was present in v10.4.0 per the changelog mentioning v10.4.0 and migration guides referencing SorobanRpc. In later versions (post-v11), SorobanRpc was deprecated in favor of a top-level rpc export: import { rpc } from '@stellar/stellar-sdk'; Note: The package transitioned to @stellar/stellar-sdk scoped name starting around v11+, but v10.4.0 uses the unscoped stellar-sdk. Always check your bundler for tree-shaking with named imports.

Citations:


🌐 Web query:

stellar-sdk 10.4.0 "new Server" SOROBAN_RPC vs SorobanRpc.Server

💡 Result:

In the stellar-sdk JavaScript/TypeScript library (likely @stellar/stellar-sdk, as the legacy 'stellar-sdk' package reached v13.x), the primary way to create a Soroban RPC client (now Stellar RPC) is via new Server(url, options), where Server is from the rpc module. SorobanRpc.Server refers to the Server class in the SorobanRpc namespace, used in older or specific contexts like the deprecated js-soroban-client library. Example: new SorobanRpc.Server('http://localhost:8000/soroban/rpc'). "SOROBAN_RPC" likely refers to the Soroban RPC protocol or environment variable/constant, while new Server is the constructor call for the client. No specific changes or differences are noted in version 10.4.0 (which exists in the old stellar-sdk package per changelogs), as RPC client usage has remained consistent: const server = new StellarSdk.rpc.Server(rpcUrl); or similar import. Official docs confirm new Server(serverURL) for RPC connections, with options like {allowHttp: true} for local dev. Soroban RPC was renamed to Stellar RPC in late 2024, but SDK classes retained naming. Usage example: import * as StellarSdk from '@stellar/stellar-sdk'; const server = new StellarSdk.rpc.Server('https://soroban-testnet.stellar.org'); No evidence of breaking changes to this API in recent versions; it's the standard constructor.

Citations:


Fix critical API breakage in Soroban transaction flow.

  1. contract.call() requires xdr.ScVal[] arguments (Line 265): Currently passing any[] directly. The method signature is contract.call(method: string, ...params: xdr.ScVal[]). Each argument must be a properly constructed xdr.ScVal instance (e.g., xdr.ScVal.scvU32(5) for numbers). Raw JS values will fail simulation. Change parameter type from any[] to xdr.ScVal[] and require callers to pre-convert using SDK utilities like nativeToScVal.

  2. assembleTransaction() call is broken (Line 281): The method is in the rpc module, not TransactionBuilder. Correct signature is SorobanRpc.assembleTransaction(tx, simulatedTx) (two arguments only; networkPassphrase is implicitly preserved from the transaction). The current three-argument call will throw at runtime. Replace TransactionBuilder.assembleTransaction(tx, NETWORK_PASSPHRASE, simulatedTx as any).build() with SorobanRpc.assembleTransaction(tx, simulatedTx).build().

  3. Use SorobanRpc.Api.isSimulationError() for error checking (Line 272): Replace the manual check with the SDK's discriminated union helper: if (SorobanRpc.Api.isSimulationError(simulatedTx)).

Also update imports to use SorobanRpc namespace: import { SorobanRpc, ... } from "stellar-sdk" and use new SorobanRpc.Server(SOROBAN_RPC, { allowHttp: true }) instead of plain new Server().

🔧 Proposed fix
  const invokeSorobanContract = async (
    contractId: string,
    functionName: string,
-   args: any[] = [],
+   args: xdr.ScVal[] = [],
  ) => {
    if (!address) throw new Error("No wallet connected");
    try {
-     // Dynamic import to avoid build-time issues
-     const { Contract, TimeoutInfinite } = await import("stellar-sdk");
-     
-     // Use the Soroban RPC
-     const rpcServer = new Server(SOROBAN_RPC, { allowHttp: true });
+     const rpcServer = new SorobanRpc.Server(SOROBAN_RPC, { allowHttp: true });
       const acct = await rpcServer.getAccount(address);
       const contract = new Contract(contractId);

       let tx = new TransactionBuilder(acct, {
         fee: "10000",
         networkPassphrase: NETWORK_PASSPHRASE,
       })
         .addOperation(contract.call(functionName, ...args))
         .setTimeout(TimeoutInfinite)
         .build();

       const simulatedTx = await rpcServer.simulateTransaction(tx);
-      if ("error" in simulatedTx && simulatedTx.error) {
+      if (SorobanRpc.Api.isSimulationError(simulatedTx)) {
         throw new Error(`Simulation failed: ${simulatedTx.error}`);
       }

-      // Assemble transaction with simulation data
-      if (!simulatedTx.transactionData) {
-        throw new Error("Simulation failed: missing transactionData");
-      }
-
-      tx = TransactionBuilder.assembleTransaction(tx, NETWORK_PASSPHRASE, simulatedTx as any).build();
+      tx = SorobanRpc.assembleTransaction(tx, simulatedTx).build();

Also update imports (top of file):

-import { Server, TransactionBuilder, Networks, Operation, Asset } from "stellar-sdk";
+import { SorobanRpc, TransactionBuilder, Networks, Operation, Asset } from "stellar-sdk";

And import Contract and TimeoutInfinite statically or ensure they come from the right namespace.

🤖 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 - 281,
invokeSorobanContract has three issues: contract.call is being passed raw JS
values — change the args type to xdr.ScVal[] and require callers to convert
values (use nativeToScVal/xdr.ScVal helpers) so each parameter is an xdr.ScVal
when calling contract.call(method, ...args); simulatedTx error checking should
use the SDK helper SorobanRpc.Api.isSimulationError(simulatedTx) instead of
manual property checks; and assembling the final tx must call
SorobanRpc.assembleTransaction(tx, simulatedTx).build() (not
TransactionBuilder.assembleTransaction with a networkPassphrase arg). Also
update imports to bring in SorobanRpc, xdr, nativeToScVal (and ensure you
construct the RPC client with new SorobanRpc.Server(SOROBAN_RPC, { allowHttp:
true })) so the corrected symbols are available.


// Sign with Freighter
const txXDR = tx.toXDR();
const signedEnvelopeXDR = await signWithFreighter(txXDR);
const signedTx = TransactionBuilder.fromXDR(signedEnvelopeXDR, NETWORK_PASSPHRASE);

// Submit the transaction to Soroban
const sendResponse = await rpcServer.sendTransaction(signedTx);

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

return sendResponse;
} catch (err) {
console.error("invokeSorobanContract", err);
throw err;
Expand Down
Loading