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
56 changes: 56 additions & 0 deletions src/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Keypair } from "stellar-sdk";
import { AuthRequest } from "../middleware/auth";
import { prisma } from "../config/database";
import { AppError } from "../middleware/errorHandler";
import { stellarClient } from "../services/stellar/client";

const WALLET_ENC_SALT_PREFIX = "acbu-wallet-v1:";
const WALLET_ENC_KEYLEN = 32;
Expand Down Expand Up @@ -555,3 +556,58 @@ export async function getReceiveQrcode(
next(e);
}
}

/**
* GET /users/me/balance
* Returns the authenticated user's on-chain balances (ACBU + XLM + all assets).
*/
export async function getBalance(
req: AuthRequest,
res: Response,
next: NextFunction,
): Promise<void> {
try {
const userId = req.apiKey?.userId;
if (!userId) throw new AppError("User-scoped API key required", 401);

const user = await prisma.user.findUnique({
where: { id: userId },
select: { stellarAddress: true },
});
if (!user) throw new AppError("User not found", 404);
if (!user.stellarAddress) throw new AppError("Wallet not activated", 400);

const account = await stellarClient.getAccount(user.stellarAddress);

// Determine ACBU balance (custom asset or native XLM)
const acbuIssuer = process.env.STELLAR_ACBU_ASSET_ISSUER;
let acbuBalance = "0";
let xlmBalance = "0";

for (const b of account.balances) {
if (b.asset_type === "native") {
xlmBalance = b.balance;
if (!acbuIssuer) {
// When no custom issuer, ACBU is native XLM
acbuBalance = b.balance;
}
} else if (
acbuIssuer &&
"asset_code" in b &&
b.asset_code === "ACBU" &&
"asset_issuer" in b &&
b.asset_issuer === acbuIssuer
) {
acbuBalance = b.balance;
}
}

res.json({
acbu_balance: acbuBalance,
xlm_balance: xlmBalance,
balances: account.balances,
});
} catch (e) {
next(e);
}
}
15 changes: 14 additions & 1 deletion src/jobs/walletActivationJob.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
feature/get-users-me-balance
import { someFunction } from 'some-library';
=======
/**
* Consumes WALLET_ACTIVATION queue: when user pays KYC fee, send min crypto to their address.
*
Expand All @@ -9,9 +12,18 @@ import type { ConsumeMessage } from "amqplib";
import { connectRabbitMQ, QUEUES } from "../config/rabbitmq";
import { logger } from "../config/logger";
import { sendCryptoToActivate } from "../services/wallet/walletActivationService";
main

const QUEUE = QUEUES.WALLET_ACTIVATION;
const myVariable = 'value';

feature/get-users-me-balance
export const walletActivationJob = async () => {
// Your job logic here
};

// Another comment here
const anotherVariable = 'anotherValue';
=======
export async function startWalletActivationConsumer(): Promise<void> {
const ch = await connectRabbitMQ();
await ch.assertQueue(QUEUE, { durable: true });
Expand All @@ -36,3 +48,4 @@ export async function startWalletActivationConsumer(): Promise<void> {
);
logger.info("Wallet activation consumer started", { queue: QUEUE });
}
main
2 changes: 2 additions & 0 deletions src/routes/userRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
postGuardians,
getGuardians,
deleteGuardian,
getBalance,
} from "../controllers/userController";
import { validateApiKey } from "../middleware/auth";
import { apiKeyRateLimiter } from "../middleware/rateLimiter";
Expand All @@ -22,6 +23,7 @@ router.use(validateApiKey);
router.use(apiKeyRateLimiter);

router.get("/me", getMe);
router.get("/me/balance", getBalance);
router.patch("/me", patchMe);
router.delete("/me", deleteMe);
router.get("/me/receive", getReceive);
Expand Down
Loading