Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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: 13 additions & 0 deletions backend/src/settlement/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";

@Entity("users")
export class User {
@PrimaryGeneratedColumn("uuid")
id: string;

@Column("jsonb", { default: {} })
balances: Record<string, number>; // asset β†’ amount

@Column({ type: "timestamp", nullable: true })
snoozedUntil: Date | null;
}
Comment on lines +3 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | πŸ”΄ Critical

Avoid a second User entity mapped to users.

Lines 3–13 define a new User model under backend/src/settlement/entities, but the service/repository imports resolve to backend/src/entities/user.entity.ts. This splits schema assumptions and can break snooze/balance persistence in production paths.

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/entities/user.entity.ts` around lines 3 - 13, You
added a second User entity class (class User) that maps to the same "users"
table, causing duplicate schema definitions; either remove this duplicate entity
and import the canonical User entity everywhere, or rename/repurpose this entity
and its `@Entity` name to avoid colliding with the existing User model. Locate
usages that import User from the settlement module and change them to import the
single canonical User export instead (or adjust repository/service imports to
the canonical model), and ensure only one entity is registered with the "users"
table so snoozedUntil and balances persist consistently.

29 changes: 29 additions & 0 deletions backend/src/settlement/settlement.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Router } from "express";
import { generateSuggestions, snoozeSettlement, unsnoozeSettlement, verifySettlement } from "./settlement.service";

const router = Router();

router.get("/suggestions", async (req, res) => {
const suggestions = await generateSuggestions();
res.json(suggestions);
});

router.post("/snooze", async (req, res) => {
const { userId, until } = req.body;
await snoozeSettlement(userId, new Date(until));
res.json({ success: true });
});

router.post("/unsnooze", async (req, res) => {
const { userId } = req.body;
await unsnoozeSettlement(userId);
res.json({ success: true });
Comment on lines +11 to +20
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | πŸ”΄ Critical

Missing authorization allows snoozing/unsnoozing arbitrary users.

Lines 12 and 18 accept userId directly from body with no auth binding. Any caller can target another user’s snooze state.

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/settlement.controller.ts` around lines 11 - 20, The
snooze and unsnooze endpoints accept userId from the request body allowing any
caller to modify another user’s state; change the handlers for
router.post("/snooze") and router.post("/unsnooze") to derive the target user ID
from the authenticated request (e.g., req.user.id or req.auth.userId) instead of
req.body.userId, or only allow a body userId when the caller has an admin role
(validate req.user.role), and still validate the authenticated user’s
permission; also validate the until value passed into snoozeSettlement (ensure
it’s a valid future date) and keep using snoozeSettlement(userId, new
Date(until)) and unsnoozeSettlement(userId) once the userId source is secured.

});

router.post("/verify", async (req, res) => {
const { user, asset, amount } = req.body;
const verified = await verifySettlement(user, asset, amount);
res.json({ verified });
Comment on lines +23 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | πŸ”΄ Critical

/verify trusts a client-supplied user object (spoofing risk).

Lines 24–25 accept user from request body and pass it to verification. This lets clients impersonate identities/accounts. Resolve user server-side from auth context.

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/settlement.controller.ts` around lines 23 - 26, The
/verify endpoint currently trusts a client-supplied user from req.body and
passes it to verifySettlement (router.post("/verify") -> verifySettlement),
which allows impersonation; instead retrieve the authenticated user/server-side
identity from the request auth context (e.g., req.user, req.auth, or the decoded
JWT in the middleware), validate that identity and use that server-resolved user
when calling verifySettlement, rather than any user field from req.body; also
validate/whitelist asset and amount from the body before calling
verifySettlement and return a 4xx error if the authenticated user is missing or
mismatched.

});

export default router;
22 changes: 22 additions & 0 deletions backend/src/settlement/settlement.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { AppDataSource } from "../data-source";
import { User } from "../entities/user.entity";

export const userRepo = AppDataSource.getRepository(User);

export async function findActiveParticipants(): Promise<User[]> {
const now = new Date();
return userRepo.find({
where: [
{ snoozedUntil: null },
{ snoozedUntil: { $lt: now } }
]
Comment on lines +9 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | πŸ”΄ Critical

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and read the relevant lines
if [ -f "backend/src/settlement/settlement.repository.ts" ]; then
  echo "=== File exists, reading lines 1-30 ===" 
  head -30 "backend/src/settlement/settlement.repository.ts" | cat -n
else
  echo "File not found at: backend/src/settlement/settlement.repository.ts"
  # Try to find it
  find . -name "settlement.repository.ts" -type f 2>/dev/null
fi

Repository: OlufunbiIK/StellarSplit

Length of output: 875


🏁 Script executed:

# Check the TypeORM version in package.json or package-lock.json
echo "=== Checking TypeORM version ===" 
if [ -f "package.json" ]; then
  grep -A 1 -B 1 '"typeorm"' package.json
fi

if [ -f "package-lock.json" ]; then
  echo "=== From package-lock.json ===" 
  grep -A 5 '"typeorm"' package-lock.json | head -20
fi

Repository: OlufunbiIK/StellarSplit

Length of output: 541


🏁 Script executed:

# Search for the specific query pattern in the file to understand context
echo "=== Searching for snoozedUntil queries ===" 
rg "snoozedUntil" --context 5 backend/src/settlement/ 2>/dev/null || rg "snoozedUntil" --context 5 . 2>/dev/null | head -60

Repository: OlufunbiIK/StellarSplit

Length of output: 5047


🏁 Script executed:

# Check imports in settlement.service.ts to see the pattern
echo "=== Imports in settlement.service.ts ===" 
head -20 backend/src/settlement/settlement.service.ts | cat -n

Repository: OlufunbiIK/StellarSplit

Length of output: 1087


🏁 Script executed:

# Check if LessThan and IsNull are used anywhere
echo "=== Search for LessThan/IsNull usage ===" 
rg "LessThan|IsNull" backend/src/ 2>/dev/null || echo "Not found in backend/src"

Repository: OlufunbiIK/StellarSplit

Length of output: 5183


🏁 Script executed:

# Check for any MongoDB-style operators ($lt, $eq, $gt, etc.)
echo "=== Search for MongoDB-style operators ===" 
rg '\$\w+:' backend/src/settlement/ 2>/dev/null || echo "No MongoDB operators found"

Repository: OlufunbiIK/StellarSplit

Length of output: 194


Use TypeORM FindOperators, not MongoDB-style $lt.

Line 11 uses { $lt: now }, which is invalid MongoDB syntax. TypeORM 0.3 requires FindOperator functions like LessThan() and IsNull(). This will fail at runtime.

Proposed fix
+import { LessThan, IsNull } from "typeorm";
...
   return userRepo.find({
     where: [
-      { snoozedUntil: null },
-      { snoozedUntil: { $lt: now } }
+      { snoozedUntil: IsNull() },
+      { snoozedUntil: LessThan(now) }
     ]
   });
πŸ“ 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
where: [
{ snoozedUntil: null },
{ snoozedUntil: { $lt: now } }
]
import { LessThan, IsNull } from "typeorm";
// ... other imports and code ...
where: [
{ snoozedUntil: IsNull() },
{ snoozedUntil: LessThan(now) }
]
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/settlement.repository.ts` around lines 9 - 12, Replace
the MongoDB-style `{ $lt: now }` with TypeORM FindOperators: import and use
LessThan(now) and IsNull() in the query where clause so it becomes something
like `where: [{ snoozedUntil: IsNull() }, { snoozedUntil: LessThan(now) }]`;
ensure the import of LessThan and IsNull from 'typeorm' and update the
repository query (the `where` object handling snoozedUntil in
settlement.repository.ts) accordingly.

});
}

export async function updateSnooze(userId: string, untilDate: Date) {
await userRepo.update(userId, { snoozedUntil: untilDate });
}

export async function clearSnooze(userId: string) {
await userRepo.update(userId, { snoozedUntil: null });
}
Comment on lines +16 to +22
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate the user repository to understand the update() method
find . -type f -name "*.ts" | grep -i "user.*repo" | head -20

Repository: OlufunbiIK/StellarSplit

Length of output: 49


🏁 Script executed:

# Also search for the settlement repository and related files
find . -type f -name "*.ts" -path "*/settlement/*"

Repository: OlufunbiIK/StellarSplit

Length of output: 648


🏁 Script executed:

# Search for userRepo definition and update method
rg "userRepo\s*=" --type ts -A 3 -B 3 | head -50

Repository: OlufunbiIK/StellarSplit

Length of output: 651


🏁 Script executed:

# Look for the update method in repository implementations
rg "\.update\(" --type ts -B 2 -A 5 | head -100

Repository: OlufunbiIK/StellarSplit

Length of output: 7130


🏁 Script executed:

# Read the settlement.repository.ts file to see full context
cat -n backend/src/settlement/settlement.repository.ts

Repository: OlufunbiIK/StellarSplit

Length of output: 835


🏁 Script executed:

# Search for all userRepo.update calls in the codebase
rg "userRepo\.update" --type ts -B 2 -A 5

Repository: OlufunbiIK/StellarSplit

Length of output: 1304


🏁 Script executed:

# Search for TypeORM update patterns and error handling
rg "\.update\(" --type ts -B 1 -A 3 | grep -A 3 -B 1 "affected\|UpdateResult"

Repository: OlufunbiIK/StellarSplit

Length of output: 49


🏁 Script executed:

# Search for calls to updateSnooze and clearSnooze to see if there's validation before
rg "updateSnooze|clearSnooze" --type ts -B 3 -A 1

Repository: OlufunbiIK/StellarSplit

Length of output: 1610


🏁 Script executed:

# Check settlement.service.ts to see the full context of how updateSnooze is called
cat -n backend/src/settlement/settlement.service.ts | head -100

Repository: OlufunbiIK/StellarSplit

Length of output: 4257


🏁 Script executed:

# Search for TypeORM UpdateResult or examples of checking affected
rg "affected|UpdateResult" --type ts

Repository: OlufunbiIK/StellarSplit

Length of output: 4194


🏁 Script executed:

# Check if NotFoundException is used in settlement module
rg "NotFoundException" backend/src/settlement/ --type ts

Repository: OlufunbiIK/StellarSplit

Length of output: 250


🏁 Script executed:

# Check settlement.controller.ts to see how snooze endpoints are used
cat -n backend/src/settlement/settlement.controller.ts

Repository: OlufunbiIK/StellarSplit

Length of output: 2888


Handle "user not found" on snooze updates.

Lines 17 and 21 don't check the update result. TypeORM's update() returns an UpdateResult with an affected property, but these functions ignore it. If the user doesn't exist, the update silently succeeds with affected: 0, hiding failures and breaking snooze guarantees. The codebase already establishes this pattern elsewhere (e.g., soft-delete.service.ts, export.service.ts).

Proposed fix
 export async function updateSnooze(userId: string, untilDate: Date) {
-  await userRepo.update(userId, { snoozedUntil: untilDate });
+  const result = await userRepo.update(userId, { snoozedUntil: untilDate });
+  if (!result.affected) throw new NotFoundException("User not found");
 }
 
 export async function clearSnooze(userId: string) {
-  await userRepo.update(userId, { snoozedUntil: null });
+  const result = await userRepo.update(userId, { snoozedUntil: null });
+  if (!result.affected) throw new NotFoundException("User not found");
 }
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/settlement.repository.ts` around lines 16 - 22, The
updateSnooze and clearSnooze functions currently ignore TypeORM's UpdateResult
so a missing user is silently ignored; call userRepo.update(...) and inspect the
returned UpdateResult. If result.affected === 0, throw a suitable not-found
error (or return an error) so callers know the user was not found; updateSnooze
and clearSnooze should both perform this check after the call to
userRepo.update. Ensure you reference the UpdateResult.affected property when
implementing the check.

39 changes: 39 additions & 0 deletions backend/src/settlement/settlement.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { findActiveParticipants, updateSnooze, clearSnooze } from "./settlement.repository";
import { verifyBalance } from "../utils/stellarVerification";
import { User } from "../entities/user.entity";

export interface SettlementSuggestion {
participants: string[];
status: "partial" | "completed" | "invalid";
details: string;
}

export async function generateSuggestions(): Promise<SettlementSuggestion[]> {
const participants = await findActiveParticipants();
const suggestions: SettlementSuggestion[] = [];

// Example deterministic logic
const total = participants.reduce((sum, u) => sum + Object.values(u.balances).reduce((a, b) => a + b, 0), 0);

if (total === 0) {
suggestions.push({ participants: participants.map(p => p.id), status: "completed", details: "All balances settled" });
} else if (total > 0) {
suggestions.push({ participants: participants.map(p => p.id), status: "partial", details: "Some balances remain unsettled" });
} else {
suggestions.push({ participants: participants.map(p => p.id), status: "invalid", details: "Balances mismatch" });
}
Comment on lines +15 to 24
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | πŸ”΄ Critical

Settlement status logic is still placeholder and under-spec’d.

Line 15 explicitly marks placeholder logic, and Lines 18–23 classify outcomes only by global sum sign. That does not robustly cover partial/completed/invalid match scenarios, and Line 18 (total === 0) is brittle with decimal balances.

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/settlement.service.ts` around lines 15 - 24, The
current placeholder in settlement.service.ts uses a single global sum (const
total = participants.reduce(...)) and strict equality to 0 to decide status,
which is brittle for decimal balances and doesn't cover per-participant
inconsistencies; replace this with robust logic in the same module/function:
compute totals in a safe numeric domain (use integer cents or a BigNumber
library) and an EPS tolerance for decimal comparisons, verify each participant's
net balance is within EPS to mark them as settled, detect any participants with
conflicting signs to mark the suggestion as "partial", and if any arithmetic
mismatch remains mark "invalid"; update the code that pushes into suggestions
(the array mutated by this logic) to include per-participant settlement
flags/details and use function/variable names participants, balances, total, and
suggestions so the fix is easy to locate.


return suggestions;
}

export async function snoozeSettlement(userId: string, until: Date) {
await updateSnooze(userId, until);
}

export async function unsnoozeSettlement(userId: string) {
await clearSnooze(userId);
}

export async function verifySettlement(user: User, asset: string, amount: number): Promise<boolean> {
return verifyBalance(user.id, asset, amount);
Comment on lines +37 to +38
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | πŸ”΄ Critical

🧩 Analysis chain

🏁 Script executed:

find backend -name "settlement.service.ts" -type f

Repository: OlufunbiIK/StellarSplit

Length of output: 111


🏁 Script executed:

cat -n backend/src/settlement/settlement.service.ts

Repository: OlufunbiIK/StellarSplit

Length of output: 6764


🏁 Script executed:

find backend -name "user.entity.ts" -type f

Repository: OlufunbiIK/StellarSplit

Length of output: 149


🏁 Script executed:

cat -n backend/src/entities/user.entity.ts

Repository: OlufunbiIK/StellarSplit

Length of output: 1206


🏁 Script executed:

find backend -name "stellarVerification.ts" -o -name "stellarVerification.js"

Repository: OlufunbiIK/StellarSplit

Length of output: 107


🏁 Script executed:

cat -n backend/src/utils/stellarVerification.ts

Repository: OlufunbiIK/StellarSplit

Length of output: 534


🏁 Script executed:

rg -A 5 "stellarAccountId\|walletAddress\|stellar.*account" backend/src/entities/user.entity.ts

Repository: OlufunbiIK/StellarSplit

Length of output: 49


🏁 Script executed:

rg "walletAddress|stellarAccountId|stellar.*id" backend/src/entities/ -A 2

Repository: OlufunbiIK/StellarSplit

Length of output: 244


🏁 Script executed:

cat -n backend/src/entities/participant.entity.ts | head -50

Repository: OlufunbiIK/StellarSplit

Length of output: 1423


🏁 Script executed:

rg "verifySettlement" backend/src/ -B 2 -A 2

Repository: OlufunbiIK/StellarSplit

Length of output: 1250


Pass Stellar account ID, not internal user UUID, to verifyBalance.

Line 38 passes user.id to verifyBalance, but user.id is a UUID (defined as @PrimaryGeneratedColumn("uuid") in the User entity). The verifyBalance function calls server.loadAccount(accountId), which is a Stellar Horizon API requiring a Stellar account public key. This will fail at runtime with an invalid account format error.

The User entity has no Stellar account ID field. Consider accepting stellarAccountId as a function parameter and resolving it from the authenticated request context server-side.

Example fix
-export async function verifySettlement(user: User, asset: string, amount: number): Promise<boolean> {
-  return verifyBalance(user.id, asset, amount);
+export async function verifySettlement(stellarAccountId: string, asset: string, amount: number): Promise<boolean> {
+  return verifyBalance(stellarAccountId, asset, amount);
 }

Then extract stellarAccountId from the authenticated user or participant record before calling this function.

πŸ“ 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
export async function verifySettlement(user: User, asset: string, amount: number): Promise<boolean> {
return verifyBalance(user.id, asset, amount);
export async function verifySettlement(stellarAccountId: string, asset: string, amount: number): Promise<boolean> {
return verifyBalance(stellarAccountId, asset, amount);
}
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/settlement.service.ts` around lines 37 - 38, The
verifySettlement function is passing the internal User.id (UUID) into
verifyBalance which expects a Stellar account public key; change
verifySettlement(user: User, asset: string, amount: number) to accept a
stellarAccountId (or add a new parameter stellarAccountId: string) and call
verifyBalance(stellarAccountId, asset, amount) instead of
verifyBalance(user.id,...). Update callers to resolve the Stellar public key
from the authenticated request context or participant record before invoking
verifySettlement, and ensure the User entity remains unchanged (no assumption of
a stellar field inside User).

}
25 changes: 25 additions & 0 deletions backend/src/settlement/tests/settlement.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { generateSuggestions, snoozeSettlement, unsnoozeSettlement } from "../settlement.service";
import { userRepo } from "../settlement.repository";

describe("Settlement Service", () => {
it("should generate completed suggestion when balances net to zero", async () => {
// mock participants with zero balances
const suggestions = await generateSuggestions();
expect(suggestions[0].status).toBe("completed");
});
Comment on lines +5 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

This test is not deterministic as written.

Line 6 says β€œmock participants,” but no mock/fixture is arranged. generateSuggestions() depends on repository state, and Line 8 can break on empty results.

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/tests/settlement.service.spec.ts` around lines 5 - 9,
The test calls generateSuggestions() without arranging any repository state or
mocks, so it can return an empty array and cause a flaky assertion; update the
spec to explicitly set up the required data or mock the repository methods used
by generateSuggestions (e.g., spyOn/ mockReturnValue for the participant/balance
repository methods that generateSuggestions reads) so the function returns a
non-empty suggestions array with net-zero balances, then assert
suggestions.length > 0 before checking suggestions[0].status === "completed".
Ensure you reference generateSuggestions and the repository methods it depends
on when adding the fixtures/mocks.


it("should persist snooze state", async () => {
const userId = "test-user";
const until = new Date(Date.now() + 3600 * 1000);
await snoozeSettlement(userId, until);
const user = await userRepo.findOneBy({ id: userId });
expect(user?.snoozedUntil).toEqual(until);
});

it("should clear snooze state", async () => {
const userId = "test-user";
await unsnoozeSettlement(userId);
const user = await userRepo.findOneBy({ id: userId });
expect(user?.snoozedUntil).toBeNull();
});
Comment on lines +11 to +24
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Snooze tests need a real fixture user before asserting persistence.

Lines 12/20 use a hardcoded user ID without setup. update(...) may affect zero rows, making these checks unreliable (Line 16 may fail; Line 23 may pass trivially with undefined).

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/settlement/tests/settlement.service.spec.ts` around lines 11 -
24, Tests call snoozeSettlement(userId) and unsnoozeSettlement(userId) using a
hardcoded "test-user" but never create that user, so the DB updates may affect
zero rows and assertions become unreliable; update each test (or a beforeEach)
to create a real user fixture via userRepo.save/insert with id "test-user" (and
any required fields) before calling snoozeSettlement/unsnoozeSettlement, and
optionally cleanup after the test, then ensure you assert that
userRepo.findOneBy({ id: userId }) returns a defined user before checking
snoozedUntil; reference snoozeSettlement, unsnoozeSettlement and
userRepo.findOneBy to locate where to add the fixture setup/teardown.

});
9 changes: 9 additions & 0 deletions backend/src/utils/stellarVerification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Server } from "stellar-sdk";

const server = new Server("https://horizon-testnet.stellar.org");
Comment on lines +1 to +3
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | πŸ”΄ Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f \( -name "package.json" -o -name "package-lock.json" -o -name "yarn.lock" \) | head -5

Repository: OlufunbiIK/StellarSplit

Length of output: 190


🏁 Script executed:

rg "@stellar/stellar-sdk|stellar-sdk" -t json | grep -E "(stellar-sdk|@stellar)" | head -20

Repository: OlufunbiIK/StellarSplit

Length of output: 448


🏁 Script executed:

cat backend/src/utils/stellarVerification.ts 2>/dev/null || echo "File may not exist or path may differ"

Repository: OlufunbiIK/StellarSplit

Length of output: 471


🏁 Script executed:

fd -t f "stellarVerification" backend/

Repository: OlufunbiIK/StellarSplit

Length of output: 107


🌐 Web query:

@stellar/stellar-sdk v14 Server import correct usage documentation

πŸ’‘ Result:

The correct usage for importing and using the Server class from @stellar/stellar-sdk v14 (latest v14.6.1 as of 2026-03-04) is as follows: Installation: npm install --save @stellar/stellar-sdk Import (ES6 modules): import * as StellarSdk from '@stellar/stellar-sdk'; // or for tree-shaking: import { Server } from '@stellar/stellar-sdk'; CommonJS: const StellarSdk = require('@stellar/stellar-sdk'); Usage examples: For Horizon Server (REST API): const server = new StellarSdk.Server('https://horizon-testnet.stellar.org'); For Soroban RPC Server: const server = new StellarSdk.Server('https://soroban-testnet.stellar.org'); Example - Load account: const account = await server.getAccount('GBZC6Y2Y7Q3ZQ2Y4QZJ2XZ3Z5YXZ6Z7Z2Y4QZJ2XZ3Z5YXZ6Z7Z2Y4'); console.log('sequence:', account.sequence); There are two Server classes: one for Horizon (lib/horizon/server.js) and one for Soroban RPC (lib/rpc/server.js), both exported from the main package and accessible via StellarSdk.Server. Use the appropriate server URL to instantiate the correct type. Official documentation: https://stellar.github.io/js-stellar-sdk/

Citations:


🏁 Script executed:

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

Repository: OlufunbiIK/StellarSplit

Length of output: 455


🏁 Script executed:

# Check if there's a type definition or barrel export in node_modules or tsconfig
find backend -type f \( -name "tsconfig.json" -o -name "*.d.ts" \) 2>/dev/null | head -10

Repository: OlufunbiIK/StellarSplit

Length of output: 204


🏁 Script executed:

# Look for any other imports of Server or Horizon in the codebase to see what pattern is actually used
rg "import.*Server|import.*Horizon" backend/ --type ts --type tsx

Repository: OlufunbiIK/StellarSplit

Length of output: 94


🏁 Script executed:

rg "import.*Server" backend/ --type ts

Repository: OlufunbiIK/StellarSplit

Length of output: 786


🏁 Script executed:

rg "Server|Horizon" backend/src/utils/stellarVerification.ts

Repository: OlufunbiIK/StellarSplit

Length of output: 170


Fix incorrect Stellar SDK package import.

Line 1 imports from "stellar-sdk" which does not exist. The installed package is @stellar/stellar-sdk v14.4.3. Change the import to use the correct package name:

Proposed fix
-import { Server } from "stellar-sdk";
+import { Server } from "@stellar/stellar-sdk";
 
 const server = new Server("https://horizon-testnet.stellar.org");
πŸ“ 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
import { Server } from "stellar-sdk";
const server = new Server("https://horizon-testnet.stellar.org");
import { Server } from "@stellar/stellar-sdk";
const server = new Server("https://horizon-testnet.stellar.org");
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/utils/stellarVerification.ts` around lines 1 - 3, The import at
the top of stellarVerification.ts is wrongβ€”replace the module specifier
"stellar-sdk" with the installed package "@stellar/stellar-sdk" so the Server
class resolves; update the import statement that currently references Server and
the existing const server = new Server(...) to import Server from
"@stellar/stellar-sdk" instead.


export async function verifyBalance(accountId: string, asset: string, expected: number): Promise<boolean> {
const account = await server.loadAccount(accountId);
const balance = account.balances.find(b => b.asset_code === asset);
return balance ? Number(balance.balance) >= expected : false;
}
Comment on lines +5 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n backend/src/utils/stellarVerification.ts

Repository: OlufunbiIK/StellarSplit

Length of output: 534


🏁 Script executed:

# Search for imports and server definition
rg -n "import|server" backend/src/utils/stellarVerification.ts -A 2 -B 2

Repository: OlufunbiIK/StellarSplit

Length of output: 485


🏁 Script executed:

# Check if there are any tests or related files that use this function
fd -e test -e spec | xargs rg -l "verifyBalance" 2>/dev/null || echo "No test files found"

Repository: OlufunbiIK/StellarSplit

Length of output: 152


🏁 Script executed:

# Search for error handling patterns around this function in the codebase
rg -n "loadAccount|balances\.find" backend/src --type ts -A 2 -B 2 | head -50

Repository: OlufunbiIK/StellarSplit

Length of output: 1050


🏁 Script executed:

# Check the broader context of how this function is used
rg -n "verifyBalance" backend/src --type ts -B 3 -A 3

Repository: OlufunbiIK/StellarSplit

Length of output: 1750


🏁 Script executed:

# Search for any test files or documentation mentioning native assets or balances
rg -n "native|asset_code|asset_type" backend/src --type ts -B 2 -A 2 | head -60

Repository: OlufunbiIK/StellarSplit

Length of output: 5761


🌐 Web query:

Stellar SDK native asset balances asset_code structure

πŸ’‘ Result:

In the Stellar SDK, native asset balances are retrieved via the Horizon API by querying an account endpoint (e.g., server.loadAccount(accountId)), which returns an AccountResponse containing a 'balances' array. Each balance object in the array has this structure: For the native asset (XLM): - balance: string (e.g., "9997.6802725") - asset_type: "native" - No asset_code or asset_issuer For issued (non-native) assets: - balance: string (e.g., "126.8107491") - limit: string (e.g., "5000.0000000") - trust limit - asset_type: "credit_alphanum4" or "credit_alphanum12" - asset_code: string (e.g., "BAR", "FOO") - up to 4 or 12 alphanumeric chars - asset_issuer: string (account ID, e.g., "GBAUUA74H4XOQYRSOW2RZUA4QL5PB37U3JS5NE3RTB2ELJVMIF5RLMAG") Additional fields may include buying_liabilities, selling_liabilities, last_modified_ledger. The Stellar SDK Asset class constructor is new Asset(code: string, issuer: string), with Asset.native for native. Asset codes are 1-12 alphanumeric chars for issued assets; native has no code.

Citations:


🏁 Script executed:

# Check if there are any balance-related tests or examples
fd -name "*.test.ts" -o -name "*.spec.ts" | xargs rg -l "balance" 2>/dev/null || echo "No balance tests found"

Repository: OlufunbiIK/StellarSplit

Length of output: 1747


🏁 Script executed:

# Look at the settlement.service to understand the context better
cat -n backend/src/settlement/settlement.service.ts | head -50

Repository: OlufunbiIK/StellarSplit

Length of output: 2364


Add error handling and support native XLM balances.

Line 6 (server.loadAccount()) lacks error handling for network failures and invalid accounts. Additionally, line 7 only checks asset_code, which doesn't exist on native XLM balances (these have asset_type: "native" instead). This causes the function to return false when verifying XLM, rather than actually checking the balance. The codebase correctly handles this distinction elsewhere (see stellar.service.ts and debt-simplification.service.ts).

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/utils/stellarVerification.ts` around lines 5 - 9, The
verifyBalance function should handle network/invalid-account errors and
correctly detect native XLM balances: wrap the call to
server.loadAccount(accountId) in a try/catch and return false (or handle/log) on
failure instead of letting the exception bubble; when scanning account.balances,
match a balance by treating native XLM specially (check b.asset_type ===
'native' for asset === 'XLM') in addition to matching b.asset_code === asset,
then compare Number(b.balance) >= expected; update verifyBalance to use these
checks so server.loadAccount, account.balances, asset_code and asset_type are
handled correctly.