Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
1aa9f30
add new rebalancing flow
gianfra-t Jun 1, 2026
0babd16
fix crash recovery for squidrouter phase
gianfra-t Jun 1, 2026
60ba847
pass slack token explicitely
gianfra-t Jun 1, 2026
35cbbe1
add better state management
gianfra-t Jun 1, 2026
83b3fe2
improve slack message
gianfra-t Jun 2, 2026
a19482d
store rebalancing history for daily cap
gianfra-t Jun 2, 2026
c94a54b
Potential fix for pull request finding
gianfra-t Jun 2, 2026
fdf11f6
Potential fix for pull request finding 'Unused variable, import, func…
gianfra-t Jun 2, 2026
ff23f5a
Potential fix for pull request finding 'Unused variable, import, func…
gianfra-t Jun 2, 2026
dcdde20
improve state handling, "force" feature flow control
gianfra-t Jun 2, 2026
9abc3ad
Update spec
ebma Jun 3, 2026
5224509
Improve and refactor code
ebma Jun 3, 2026
01eed2d
fix base rebalancer balance checks
ebma Jun 3, 2026
b8ec44a
Update rebalancer spec
ebma Jun 3, 2026
7304909
Adjust notification layout
ebma Jun 3, 2026
e2ec13e
Potential fix for pull request finding
gianfra-t Jun 3, 2026
09a612e
Potential fix for pull request finding
gianfra-t Jun 3, 2026
eaeac22
Potential fix for pull request finding
gianfra-t Jun 3, 2026
a0e0d3a
Potential fix for pull request finding
gianfra-t Jun 3, 2026
7f1a5ca
Potential fix for pull request finding
gianfra-t Jun 3, 2026
5d8fbba
add main nabla rebalance route
gianfra-t Jun 4, 2026
6d4a92d
add brla-usdc rebalance direction
gianfra-t Jun 4, 2026
2ec2c38
Potential fix for pull request finding 'Unused variable, import, func…
gianfra-t Jun 5, 2026
1ed7074
fix type issue
gianfra-t Jun 5, 2026
8da9773
re-order BRLA -> USDC rebalance steps, fixes
gianfra-t Jun 5, 2026
d9e78ca
revert testing change
gianfra-t Jun 5, 2026
3fefc6e
Merge branch 'staging' into add-base-rebalancer-flow
gianfra-t Jun 8, 2026
931b638
feat(shared): make BrlaCreateSubaccountRequest.quoteId optional
Sharqiewicz Jun 10, 2026
33f0ea8
feat(frontend): quote-less KYB deep link with region selector
Sharqiewicz Jun 10, 2026
9503137
fix(frontend): KYB deep-link back button returns to region selector
Sharqiewicz Jun 11, 2026
fd50982
core fix: kybLink consolidation + reset bug + machine cleanups
Sharqiewicz Jun 11, 2026
9c0236b
fix(frontend): hide back button on KYB region selector
Sharqiewicz Jun 11, 2026
11f3f61
fix(frontend): forward externalSessionId/partnerId/apiKey in KYB deep…
Sharqiewicz Jun 11, 2026
49b156e
feat(frontend): default widget locale from pinned KYB region (?kybLoc…
Sharqiewicz Jun 11, 2026
87024e5
style(frontend): animate dropdown press scale, skip initial mount ani…
Sharqiewicz Jun 11, 2026
5f913b8
feat(frontend): default widget locale from pinned KYB region (?kybLoc…
Sharqiewicz Jun 11, 2026
d2c4786
feat(frontend): collect Brazil KYB CNPJ on the company form
Sharqiewicz Jun 15, 2026
b427add
fix(frontend): route MX/CO business KYB deep links to the company form
Sharqiewicz Jun 15, 2026
95f0f9f
fix(frontend): hide back button on locked KYB entry screen
Sharqiewicz Jun 15, 2026
7ad7653
docs(api): document quote-less KYB deep link
Sharqiewicz Jun 15, 2026
441830d
Update rebalancing daily bridge limit to 20,000
ebma Jun 15, 2026
f4ca85c
Merge branch 'staging' into feat/kyb-deep-link
Sharqiewicz Jun 15, 2026
c88e572
improve basic Sentry config
Sharqiewicz Jun 15, 2026
d8b3484
Change default daily bridge limit back to 10k
ebma Jun 15, 2026
2ad7b17
Merge pull request #1224 from pendulum-chain/chore/sentry
ebma Jun 16, 2026
d77b555
Update security-spec for rebalancer
ebma Jun 16, 2026
51d4d0f
Fix parsing of rebalancing daily volume limit
ebma Jun 16, 2026
19fd3e8
Add rebalancer cost policy guards
ebma Jun 16, 2026
7b74acb
Add rebalancer cost policy config
ebma Jun 16, 2026
32e0ce2
Apply cost policy to base rebalancer
ebma Jun 16, 2026
9c4c787
Document cost-aware rebalancer policy
ebma Jun 16, 2026
3c6072d
Add handling for when received squidrouter amount is lower than expected
ebma Jun 16, 2026
3057b42
Adjust formatting of slack notification
ebma Jun 16, 2026
393c05a
Adjust notification again
ebma Jun 16, 2026
3fd843c
unify @scure/bip39 package version in the project
Sharqiewicz Jun 16, 2026
79a48e6
fix bug in nabla minimum output calculation
gianfra-t Jun 15, 2026
90c9bb8
Revert "fix bug in nabla minimum output calculation"
gianfra-t Jun 16, 2026
ccd7bc7
fix bug in nabla minimum output calculation
gianfra-t Jun 15, 2026
ff9dbc6
Address PR review comments
ebma Jun 16, 2026
cb0a761
Merge pull request #1225 from pendulum-chain/chore/scure-bip-version
ebma Jun 16, 2026
b9077ea
Add missing translation strings
ebma Jun 16, 2026
c935ab5
Merge pull request #1215 from pendulum-chain/feat/kyb-deep-link
ebma Jun 16, 2026
728ec84
Allow rebalancer to bypass daily volume limit for profitable quotes
ebma Jun 16, 2026
05919d1
Potential fix for pull request finding 'Unused variable, import, func…
ebma Jun 17, 2026
1a63175
Merge pull request #1184 from pendulum-chain/add-base-rebalancer-flow
ebma Jun 17, 2026
96e69a3
Add biome-ignore and tests
ebma Jun 17, 2026
e0dddbf
Update security spec
ebma Jun 17, 2026
701e825
Refactor so that `ammOutputAmountDecimal/Raw` are always assigned in …
ebma Jun 17, 2026
040ad0c
Fix type issues
ebma Jun 17, 2026
b5d9b0e
Merge pull request #1226 from pendulum-chain/fix-nabla-min-output
ebma Jun 17, 2026
44e1713
Exclude generated TypeChain files from Biome
ebma Jun 17, 2026
15bd88d
Use module augmentation for Express request fields
ebma Jun 17, 2026
f2604dc
Tighten API controller lint types
ebma Jun 17, 2026
392af4a
Tighten API auth and model types
ebma Jun 17, 2026
45ba134
Type phase handler error paths
ebma Jun 17, 2026
281d017
Remove quote engine non-null assertions
ebma Jun 17, 2026
795af9e
Guard Alfredpay EVM transaction metadata
ebma Jun 17, 2026
555d87a
Type migration recovery helpers
ebma Jun 17, 2026
90ec612
Log spreadsheet header lookup failures
ebma Jun 17, 2026
f8d5538
Improve frontend component lint compliance
ebma Jun 17, 2026
c3452f5
Tighten frontend machine and service types
ebma Jun 17, 2026
a33406e
Type Storybook mocks
ebma Jun 17, 2026
6893cb4
Tighten BRLA rebalancer lint types
ebma Jun 17, 2026
1e65a62
Clean relayer script error handling
ebma Jun 17, 2026
ab09485
Tighten SDK lint types
ebma Jun 17, 2026
7f18db9
Type shared package lint boundaries
ebma Jun 17, 2026
1a97693
Restore Express request augmentation pattern
ebma Jun 17, 2026
ad88c25
Add keyboard support to comparison slider
ebma Jun 17, 2026
97f8037
Scope Biome verification inputs
ebma Jun 17, 2026
9715291
Run Biome checks in CI
ebma Jun 17, 2026
d68e728
Remove invalid frontend ARIA roles
ebma Jun 17, 2026
429921f
Clarify review feedback types
ebma Jun 17, 2026
181f514
Import React story types explicitly
ebma Jun 17, 2026
5e1034d
Include spreadsheet header error details
ebma Jun 17, 2026
c865729
Add keyboard paths for hover UI
ebma Jun 17, 2026
89e74c9
Address PR #1227 review feedback
ebma Jun 17, 2026
ea75fec
Simplify addIndex option name check
ebma Jun 17, 2026
ac545a0
Add fast forex spec document
ebma Jun 17, 2026
0908261
Handle missing users and poll errors
ebma Jun 17, 2026
ecc1cbe
Merge pull request #1227 from pendulum-chain/fix-biome-issues
ebma Jun 17, 2026
cf9b8bb
Implement opportunistic rebalancing logic and fallback route evaluation
ebma Jun 17, 2026
3e909e5
Address PR comments and refactor
ebma Jun 18, 2026
bc3f7b9
Add daily bridge limit context retrieval for opportunistic rebalancing
ebma Jun 18, 2026
55c6178
Enhance opportunistic rebalancing with fallback context and state man…
ebma Jun 18, 2026
8b4de35
Refactor daily bridge limit evaluation and update related logic for p…
ebma Jun 18, 2026
c058d8d
Add missing module
ebma Jun 18, 2026
5c195d7
Address PR` feedback
ebma Jun 18, 2026
9914d14
Merge pull request #1229 from pendulum-chain/improve-rebalancer-again
ebma Jun 18, 2026
af0dddd
Add discount fields to shared responses
ebma Jun 19, 2026
10c8a59
Serialize quote discount display data
ebma Jun 19, 2026
daacf6a
Expose discount fields on API surfaces
ebma Jun 19, 2026
17e8d4e
Show discount in quote fee details
ebma Jun 19, 2026
0842dfd
Show discount in confirmation fee details
ebma Jun 19, 2026
e015f07
Document discount display invariants
ebma Jun 19, 2026
d32ca66
Fix discount tooltip overflow
ebma Jun 19, 2026
174c765
Polish discount fee display
ebma Jun 19, 2026
a0a0921
Implement BRLA output recovery logic for Nabla swaps and enhance erro…
ebma Jun 21, 2026
10df72e
Omit invalid discount display conversions
ebma Jun 22, 2026
e3d0d6d
Type ramp discount currency
ebma Jun 22, 2026
2789bf9
Show discount as fee deduction
ebma Jun 22, 2026
6964672
Align effective fee displays
ebma Jun 22, 2026
b5e1165
Fix discount story and rate types
ebma Jun 22, 2026
6348e51
Refine discount tooltip copy
ebma Jun 22, 2026
9a5867a
Merge pull request #1234 from pendulum-chain/show-discount-in-ui
ebma Jun 22, 2026
398c417
Implement BRLA transfer recovery logic and update balance arrival checks
ebma Jun 22, 2026
3bd5b2e
Model Avenia partial-failed ticket status
ebma Jun 23, 2026
746a3b0
Retry partial Avenia Polygon transfers
ebma Jun 23, 2026
25f5600
Document Avenia ticket recovery behavior
ebma Jun 23, 2026
7845a0d
Clarify Base rebalancer preflight quoting
ebma Jun 23, 2026
0a5a439
Merge pull request #1235 from pendulum-chain/add-handling-for-partial…
ebma Jun 23, 2026
e361581
Improve the rebalancer slack notification
ebma Jun 23, 2026
959280e
Add resetFailedSquidRouterSwapOnResume function to handle failed swaps
ebma Jun 23, 2026
08866d9
Improve recovery of squidrouter transfers
ebma Jun 23, 2026
f3de225
Amend rebalancer improve
ebma Jun 23, 2026
4b86574
Improve wording of rebalancer log message
ebma Jun 23, 2026
7d5c8b2
Fix log ambiguity
ebma Jun 23, 2026
3d8c6f1
Remove PR_SUMMARY.md
ebma Jun 24, 2026
3a91d6e
Fix issue with token selection dialog animation
ebma Jun 24, 2026
6678713
Make rebalancer recover from avenia to polygon transfer issues
ebma Jun 24, 2026
8d5645c
Address PR review feedback
ebma Jun 24, 2026
b9b59fe
Fix animation/transition also for ramp history and settings menu
ebma Jun 24, 2026
40a7609
Change order of discount and effective fee on checkout page
ebma Jun 24, 2026
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
7 changes: 3 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ jobs:
- name: Build
run: bun run build

- name: 🧪 Biome check
run: bun run verify

- name: ✏️ Typecheck
run: bun run typecheck

# We currently do not run the linter as we have too many issues.
# - name: 🧪 Lint
# run: bun run lint
2 changes: 1 addition & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@polkadot/keyring": "catalog:",
"@polkadot/util": "catalog:",
"@polkadot/util-crypto": "catalog:",
"@scure/bip39": "^1.5.4",
"@scure/bip39": "catalog:",
"@supabase/supabase-js": "catalog:",
"@types/multer": "^2.1.0",
"@vortexfi/shared": "workspace:*",
Expand Down
62 changes: 38 additions & 24 deletions apps/api/src/api/controllers/alfredpay.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ import logger from "../../config/logger";
import AlfredPayCustomer from "../../models/alfredPayCustomer.model";

export class AlfredpayController {
private static getRequiredUserId(req: Request): string {
if (!req.userId) {
throw new Error("Authenticated user id not available");
}

return req.userId;
}

private static getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}

private static mapKycStatus(status: AlfredpayKycStatus): AlfredPayStatus | null {
switch (status) {
case AlfredpayKycStatus.IN_REVIEW:
Expand Down Expand Up @@ -61,7 +73,7 @@ export class AlfredpayController {
static async alfredpayStatus(req: Request, res: Response) {
try {
const { country } = req.query as unknown as AlfredpayStatusRequest;
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
order: [["updatedAt", "DESC"]],
Expand Down Expand Up @@ -107,7 +119,7 @@ export class AlfredpayController {

// If the upstream API returns 404 (KYC submission not found), the local status is stale.
// Reset to Consulted so the frontend re-triggers the KYC flow.
const errorMessage = ((error as any)?.message || (error as any)?.toString() || "").toLowerCase();
const errorMessage = AlfredpayController.getErrorMessage(error).toLowerCase();
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
if (alfredPayCustomer.status === AlfredPayStatus.Success) {
logger.info("Resetting stale AlfredPay status to Consulted due to upstream 404");
Expand All @@ -132,7 +144,7 @@ export class AlfredpayController {
static async createIndividualCustomer(req: Request, res: Response) {
try {
const { country } = req.body as AlfredpayCreateCustomerRequest;
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);
const userEmail = req.userEmail;

if (!userEmail) {
Expand Down Expand Up @@ -187,7 +199,7 @@ export class AlfredpayController {
static async getKycRedirectLink(req: Request, res: Response) {
try {
const { country } = req.query as unknown as AlfredpayGetKycRedirectLinkRequest;
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
where: { country: country as AlfredPayCountry, userId }
Expand All @@ -211,7 +223,7 @@ export class AlfredpayController {
return res.status(400).json({ error: `KYC is in status ${statusRes.status}` });
}
}
} catch (e) {
} catch {
logger.info("No previous KYC submission found or error fetching it, proceeding.");
}

Expand All @@ -228,7 +240,7 @@ export class AlfredpayController {
static async kycRedirectOpened(req: Request, res: Response) {
try {
const { country, type } = req.body as unknown as { country: string; type?: AlfredpayCustomerType };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);
const selectedType = type || AlfredpayCustomerType.INDIVIDUAL;

const alfredPayCustomer = await AlfredPayCustomer.findOne({
Expand All @@ -252,7 +264,7 @@ export class AlfredpayController {
static async kycRedirectFinished(req: Request, res: Response) {
try {
const { country, type } = req.body as unknown as { country: string; type?: AlfredpayCustomerType };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);
const selectedType = type || AlfredpayCustomerType.INDIVIDUAL;

const alfredPayCustomer = await AlfredPayCustomer.findOne({
Expand All @@ -276,7 +288,7 @@ export class AlfredpayController {
static async getKycStatus(req: Request, res: Response) {
try {
const { country, type } = req.query as unknown as { country: string; type?: AlfredpayCustomerType };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);
const selectedType = type || AlfredpayCustomerType.INDIVIDUAL;

const alfredPayCustomer = await AlfredPayCustomer.findOne({
Expand All @@ -303,7 +315,9 @@ export class AlfredpayController {
? await alfredpayService.getKybStatus(alfredPayCustomer.alfredPayId, lastSubmission.submissionId)
: await alfredpayService.getKycStatus(alfredPayCustomer.alfredPayId, lastSubmission.submissionId);

const newStatus = AlfredpayController.mapKycStatus(statusResponse.status);
const newStatus = isBusiness
? AlfredpayController.mapKybStatus(statusResponse.status)
: AlfredpayController.mapKycStatus(statusResponse.status);
const updateData: Partial<AlfredPayCustomer> = {};

if (newStatus && newStatus !== alfredPayCustomer.status) {
Expand Down Expand Up @@ -336,7 +350,7 @@ export class AlfredpayController {
static async retryKyc(req: Request, res: Response) {
try {
const { country, type } = req.body as { country: string; type?: AlfredpayCustomerType };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);
const selectedType = type || AlfredpayCustomerType.INDIVIDUAL;

const alfredPayCustomer = await AlfredPayCustomer.findOne({
Expand Down Expand Up @@ -392,7 +406,7 @@ export class AlfredpayController {
static async createBusinessCustomer(req: Request, res: Response) {
try {
const { country } = req.body as { country: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);
const userEmail = req.userEmail;

if (!userEmail) {
Expand Down Expand Up @@ -448,7 +462,7 @@ export class AlfredpayController {
static async getKybRedirectLink(req: Request, res: Response) {
try {
const { country } = req.query as unknown as AlfredpayGetKycRedirectLinkRequest;
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
where: { country: country as AlfredPayCountry, type: AlfredpayCustomerType.BUSINESS, userId }
Expand All @@ -472,7 +486,7 @@ export class AlfredpayController {
return res.status(400).json({ error: `KYB is in status ${statusRes.status}` });
}
}
} catch (e) {
} catch {
logger.info("No previous KYB submission found or error fetching it, proceeding.");
}

Expand All @@ -488,7 +502,7 @@ export class AlfredpayController {
static async submitKycInformation(req: Request, res: Response) {
try {
const { country, ...kycData } = req.body as SubmitKycInformationRequest;
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
where: { country: country as AlfredPayCountry, type: AlfredpayCustomerType.INDIVIDUAL, userId }
Expand Down Expand Up @@ -526,7 +540,7 @@ export class AlfredpayController {
static async submitKycFile(req: Request, res: Response) {
try {
const { country, submissionId, fileType } = req.body as { country: string; submissionId: string; fileType: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

if (!req.file) {
return res.status(400).json({ error: "No file uploaded" });
Expand Down Expand Up @@ -560,7 +574,7 @@ export class AlfredpayController {
static async sendKycSubmission(req: Request, res: Response) {
try {
const { country, submissionId } = req.body as { country: string; submissionId: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
where: { country: country as AlfredPayCountry, type: AlfredpayCustomerType.INDIVIDUAL, userId }
Expand All @@ -584,7 +598,7 @@ export class AlfredpayController {
static async submitKybInformation(req: Request, res: Response) {
try {
const { country, ...kybData } = req.body as SubmitKybInformationRequest & { country: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
where: { country: country as AlfredPayCountry, type: AlfredpayCustomerType.BUSINESS, userId }
Expand Down Expand Up @@ -622,7 +636,7 @@ export class AlfredpayController {
static async findKybCustomerAndBusiness(req: Request, res: Response) {
try {
const { country } = req.query as { country: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
where: { country: country as AlfredPayCountry, type: AlfredpayCustomerType.BUSINESS, userId }
Expand Down Expand Up @@ -650,7 +664,7 @@ export class AlfredpayController {
static async submitKybFile(req: Request, res: Response) {
try {
const { country, submissionId, fileType } = req.body as { country: string; submissionId: string; fileType: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

if (!req.file) {
return res.status(400).json({ error: "No file uploaded" });
Expand Down Expand Up @@ -689,7 +703,7 @@ export class AlfredpayController {
relatedPersonId: string;
fileType: string;
};
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

if (!req.file) {
return res.status(400).json({ error: "No file uploaded" });
Expand Down Expand Up @@ -729,7 +743,7 @@ export class AlfredpayController {
static async sendKybSubmission(req: Request, res: Response) {
try {
const { country, submissionId } = req.body as { country: string; submissionId: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
where: { country: country as AlfredPayCountry, type: AlfredpayCustomerType.BUSINESS, userId }
Expand Down Expand Up @@ -774,7 +788,7 @@ export class AlfredpayController {
documentNumber,
isExternal = false
} = req.body as AlfredpayAddFiatAccountRequest;
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
order: [["updatedAt", "DESC"]],
Expand Down Expand Up @@ -860,7 +874,7 @@ export class AlfredpayController {
static async listFiatAccounts(req: Request, res: Response) {
try {
const { country } = req.query as { country: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
order: [["updatedAt", "DESC"]],
Expand All @@ -885,7 +899,7 @@ export class AlfredpayController {
try {
const { fiatAccountId } = req.params as { fiatAccountId: string };
const { country } = req.query as { country: string };
const userId = req.userId!;
const userId = AlfredpayController.getRequiredUserId(req);

const alfredPayCustomer = await AlfredPayCustomer.findOne({
order: [["updatedAt", "DESC"]],
Expand Down
38 changes: 20 additions & 18 deletions apps/api/src/api/controllers/metrics.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ export interface VolumeRow {
total_usd: number;
}

export interface MonthlyVolume extends VolumeRow {
month: string;
}

export interface ChainVolume {
chain: string;
buy_usd: number;
Expand Down Expand Up @@ -80,9 +76,10 @@ function getAnonSupabaseClient() {
return supabaseAnonClient;
}

function isAuthOrPermissionError(error: any): boolean {
const rawCode = error?.code ?? "";
const rawMessage = error?.message ?? "";
function isAuthOrPermissionError(error: unknown): boolean {
const errorRecord = error && typeof error === "object" ? (error as Record<string, unknown>) : {};
const rawCode = errorRecord.code ?? "";
const rawMessage = errorRecord.message ?? "";
const errorCode = String(rawCode).toLowerCase();
const errorMessage = String(rawMessage).toLowerCase();

Expand All @@ -100,7 +97,7 @@ function isAuthOrPermissionError(error: any): boolean {
return hasAuthOrPermissionText || has401 || has403;
}

async function rpcWithFallback<T extends any[], P extends Record<string, unknown>>(fn: string, params: P): Promise<T> {
async function rpcWithFallback<T extends unknown[], P extends Record<string, unknown>>(fn: string, params: P): Promise<T> {
const hasServiceKey = !!config.supabase.serviceRoleKey;
const hasAnonKey = !!config.supabase.anonKey;

Expand Down Expand Up @@ -164,10 +161,15 @@ async function rpcWithFallback<T extends any[], P extends Record<string, unknown
throw fallbackResult.error;
}

const zeroVolume = (key: string, keyName: "day" | "month"): any => ({
[keyName]: key,
chains: []
});
function zeroVolume(key: string, keyName: "day"): DailyVolume;
function zeroVolume(key: string, keyName: "month"): MonthlyVolume;
function zeroVolume(key: string, keyName: "day" | "month"): DailyVolume | MonthlyVolume {
return keyName === "day" ? { chains: [], day: key } : { chains: [], month: key };
}

function getErrorStack(error: unknown): string | undefined {
return error instanceof Error ? error.stack : undefined;
}

async function getMonthlyVolumes(): Promise<MonthlyVolume[]> {
const cacheKey = "monthly";
Expand Down Expand Up @@ -198,9 +200,9 @@ async function getMonthlyVolumes(): Promise<MonthlyVolume[]> {

cache.set(cacheKey, volumes, CACHE_TTL_SECONDS);
return volumes;
} catch (error: any) {
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error("Could not calculate monthly volumes", { error: errorMessage, stack: error?.stack });
logger.error("Could not calculate monthly volumes", { error: errorMessage, stack: getErrorStack(error) });
throw new Error("Could not calculate monthly volumes: " + errorMessage);
}
}
Expand Down Expand Up @@ -229,9 +231,9 @@ async function getDailyVolumes(startDate: string, endDate: string): Promise<Dail
}

return volumes;
} catch (error: any) {
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error("Could not calculate daily volumes", { error: errorMessage, stack: error?.stack });
logger.error("Could not calculate daily volumes", { error: errorMessage, stack: getErrorStack(error) });
throw new Error("Could not calculate daily volumes: " + errorMessage);
}
}
Expand All @@ -244,10 +246,10 @@ function aggregateWeekly(daily: DailyVolume[]): WeeklyVolume[] {
const startDay = chunk[0].day;
const endDay = chunk[chunk.length - 1].day;

const chainTotals = new Map<string, any>();
const chainTotals = new Map<string, ChainVolume>();

chunk.forEach(day => {
day.chains.forEach((c: any) => {
day.chains.forEach(c => {
const existing = chainTotals.get(c.chain) || { buy_usd: 0, chain: c.chain, sell_usd: 0, total_usd: 0 };
existing.buy_usd += c.buy_usd;
existing.sell_usd += c.sell_usd;
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/api/middlewares/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import logger from "../../config/logger";
import { validateSignatureAndGetMemo } from "../services/siwe.service";

declare global {
// biome-ignore lint/style/noNamespace: Express request augmentation follows the existing backend pattern.
namespace Express {
interface Request {
derivedMemo: string | null;
derivedMemo?: string | null;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/api/middlewares/supabaseAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import logger from "../../config/logger";
import { SupabaseAuthService } from "../services/auth";

declare global {
// biome-ignore lint/style/noNamespace: Express request augmentation follows the existing backend pattern.
namespace Express {
interface Request {
userId?: string;
Expand Down
3 changes: 3 additions & 0 deletions apps/api/src/api/routes/v1/quote.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ router
* @apiSuccess (Created 201) {String} partnerFeeUsd Partner fee in USD
* @apiSuccess (Created 201) {String} totalFeeUsd Total fee in USD
* @apiSuccess (Created 201) {String} processingFeeUsd Processing fee (anchor + vortex) in USD
* @apiSuccess (Created 201) {String} [discountFiat] Quote-time discount benefit in feeCurrency
* @apiSuccess (Created 201) {String} [discountUsd] Quote-time discount benefit in USD
* @apiSuccess (Created 201) {String} [discountCurrency] Currency used for discount display
* @apiSuccess (Created 201) {String} paymentMethod Payment method used for the quote
* @apiSuccess (Created 201) {Date} expiresAt Expiration date
*
Expand Down
Loading
Loading