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
41 changes: 27 additions & 14 deletions app/backend/src/onchain/aid-escrow.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ import {
BatchCreateAidPackagesDto,
} from './dto/aid-escrow.dto';
import { SorobanErrorMapper } from './utils/soroban-error.mapper';
import {
CreateAidPackageResult,
BatchCreateAidPackagesResult,
ClaimAidPackageResult,
DisburseAidPackageResult,
GetAidPackageResult,
GetAidPackageCountResult,
GetTransactionStatusResult,
} from './onchain.adapter';

/**
* AidEscrowController
Expand Down Expand Up @@ -75,13 +84,13 @@ export class AidEscrowController {
async createAidPackage(
@Body() dto: CreateAidPackageDto,
@Req() req: Request & { user?: { address?: string } },
): Promise<any> {
): Promise<CreateAidPackageResult> {
try {
const operatorAddress = req.user?.address || 'admin';
return await this.aidEscrowService.createAidPackage(dto, operatorAddress);
} catch (error) {
this.logger.error('Failed to create aid package:', error);
this.errorMapper.throwMappedError(error);
return this.errorMapper.throwMappedError(error);
}
}

Expand Down Expand Up @@ -121,7 +130,7 @@ export class AidEscrowController {
async batchCreateAidPackages(
@Body() dto: BatchCreateAidPackagesDto,
@Req() req: Request & { user?: { address?: string } },
): Promise<any> {
): Promise<BatchCreateAidPackagesResult> {
if (dto.recipientAddresses.length !== dto.amounts.length) {
throw new BadRequestException(
'Recipients and amounts arrays must have the same length',
Expand All @@ -136,7 +145,7 @@ export class AidEscrowController {
);
} catch (error) {
this.logger.error('Failed to batch create aid packages:', error);
this.errorMapper.throwMappedError(error);
return this.errorMapper.throwMappedError(error);
}
}

Expand Down Expand Up @@ -176,7 +185,7 @@ export class AidEscrowController {
async claimAidPackage(
@Param('id') packageId: string,
@Req() req: Request & { user?: { address?: string } },
): Promise<any> {
): Promise<ClaimAidPackageResult> {
const recipientAddress = req.user?.address;
if (!recipientAddress) {
throw new BadRequestException('Recipient address required');
Expand All @@ -189,7 +198,7 @@ export class AidEscrowController {
);
} catch (error) {
this.logger.error('Failed to claim aid package:', error);
this.errorMapper.throwMappedError(error);
return this.errorMapper.throwMappedError(error);
}
}

Expand Down Expand Up @@ -231,7 +240,7 @@ export class AidEscrowController {
async disburseAidPackage(
@Param('id') packageId: string,
@Req() req: Request & { user?: { address?: string } },
): Promise<any> {
): Promise<DisburseAidPackageResult> {
try {
const operatorAddress = req.user?.address || 'admin';
return await this.aidEscrowService.disburseAidPackage(
Expand All @@ -240,7 +249,7 @@ export class AidEscrowController {
);
} catch (error) {
this.logger.error('Failed to disburse aid package:', error);
this.errorMapper.throwMappedError(error);
return this.errorMapper.throwMappedError(error);
}
}

Expand Down Expand Up @@ -279,12 +288,14 @@ export class AidEscrowController {
@ApiInternalServerErrorResponse({
description: 'Failed to retrieve package.',
})
async getAidPackage(@Param('id') packageId: string): Promise<any> {
async getAidPackage(
@Param('id') packageId: string,
): Promise<GetAidPackageResult> {
try {
return await this.aidEscrowService.getAidPackage({ packageId });
} catch (error) {
this.logger.error('Failed to get aid package:', error);
this.errorMapper.throwMappedError(error);
return this.errorMapper.throwMappedError(error);
}
}

Expand Down Expand Up @@ -316,7 +327,7 @@ export class AidEscrowController {
@ApiInternalServerErrorResponse({
description: 'Failed to retrieve statistics.',
})
async getAidPackageStats(): Promise<any> {
async getAidPackageStats(): Promise<GetAidPackageCountResult> {
try {
// For now, return aggregates for a default token
// In production, this should be parameterized or determined from context
Expand All @@ -327,7 +338,7 @@ export class AidEscrowController {
});
} catch (error) {
this.logger.error('Failed to get aid package stats:', error);
this.errorMapper.throwMappedError(error);
return this.errorMapper.throwMappedError(error);
}
}

Expand Down Expand Up @@ -358,15 +369,17 @@ export class AidEscrowController {
@ApiInternalServerErrorResponse({
description: 'Failed to retrieve transaction status.',
})
async getTransactionStatus(@Param('hash') hash: string): Promise<any> {
async getTransactionStatus(
@Param('hash') hash: string,
): Promise<GetTransactionStatusResult> {
if (!hash || hash.length < 10) {
throw new BadRequestException('Invalid transaction hash');
}
try {
return await this.aidEscrowService.getTransactionStatus(hash);
} catch (error) {
this.logger.error('Failed to get transaction status:', error);
this.errorMapper.throwMappedError(error);
return this.errorMapper.throwMappedError(error);
}
}
}
19 changes: 14 additions & 5 deletions app/backend/src/onchain/interfaces/onchain-job.interface.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
import {
InitEscrowParams,
CreateClaimParams,
DisburseParams,
} from '../onchain.adapter';

export enum OnchainOperationType {
INIT_ESCROW = 'init-escrow',
CREATE_CLAIM = 'create-claim',
DISBURSE = 'disburse',
}

export interface OnchainJobData {
type: OnchainOperationType;
params: any;
export type OnchainJobInput =
| { type: OnchainOperationType.INIT_ESCROW; params: InitEscrowParams }
| { type: OnchainOperationType.CREATE_CLAIM; params: CreateClaimParams }
| { type: OnchainOperationType.DISBURSE; params: DisburseParams };

export type OnchainJobData = OnchainJobInput & {
timestamp: number;
correlationId?: string;
}
};

export interface OnchainJobResult {
success: boolean;
transactionHash?: string;
error?: string;
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}
37 changes: 34 additions & 3 deletions app/backend/src/onchain/ledger-backfill.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ export interface BackfillResult {
totalCount: number;
}

export interface BackfillLedgerEntry {
id: string;
campaignId?: string;
claimId?: string;
eventType: string;
amount: number;
note?: string;
createdAt: Date;
}

interface BackfillProgressSnapshot {
startLedger?: number;
endLedger?: number;
processed?: number;
total?: number;
}

@Injectable()
export class LedgerBackfillService {
private readonly logger = new Logger(LedgerBackfillService.name);
Expand Down Expand Up @@ -140,10 +157,21 @@ export class LedgerBackfillService {
continue;
}

// campaignId is required on BalanceLedger; skip entries we cannot
// attribute to a campaign rather than failing the whole range.
const resolvedCampaignId = entry.campaignId ?? campaignId;
if (!resolvedCampaignId) {
this.logger.warn(
`Skipping ledger entry ${entry.id}: no campaignId available`,
);
skipped++;
continue;
}

await this.prisma.balanceLedger.create({
data: {
id: entry.id,
campaignId: entry.campaignId || campaignId,
campaignId: resolvedCampaignId,
claimId: entry.claimId,
eventType: entry.eventType,
amount: entry.amount,
Expand All @@ -162,7 +190,10 @@ export class LedgerBackfillService {
return { processed, skipped };
}

private fetchLedgerRange(_startLedger: number, _endLedger: number): any[] {
private fetchLedgerRange(
_startLedger: number,
_endLedger: number,
): BackfillLedgerEntry[] {
// Placeholder for actual Horizon API call
// In production, this would query the Stellar Horizon API
return [];
Expand All @@ -176,7 +207,7 @@ export class LedgerBackfillService {
}

const state = await job.getState();
const progress = job.progress as any;
const progress = job.progress as BackfillProgressSnapshot | undefined;

return {
jobId: job.id || 'unknown',
Expand Down
28 changes: 24 additions & 4 deletions app/backend/src/onchain/ledger-reconciliation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,28 @@ export interface ReconciliationJobData {
export interface ReconciliationDiscrepancy {
ledger: number;
type: 'missing' | 'amount_mismatch' | 'count_mismatch';
expected: any;
observed: any;
expected: unknown;
observed: unknown;
severity: 'low' | 'medium' | 'high';
}

export interface OnChainLedgerEntry {
id: string;
ledger: number;
amount: number;
eventType: string;
}

interface ReconciliationProgressSnapshot {
startLedger?: number;
endLedger?: number;
totalLedgers?: number;
checkedLedgers?: number;
discrepancies?: ReconciliationDiscrepancy[];
summary?: ReconciliationReport['summary'];
actionable?: boolean;
}

export interface ReconciliationReport {
jobId: string;
startLedger: number;
Expand Down Expand Up @@ -198,7 +215,10 @@ export class LedgerReconciliationService {
};
}

private fetchOnChainData(_startLedger: number, _endLedger: number): any[] {
private fetchOnChainData(
_startLedger: number,
_endLedger: number,
): OnChainLedgerEntry[] {
// Placeholder for actual Horizon API call
// In production, this would query the Stellar Horizon API
return [];
Expand Down Expand Up @@ -231,7 +251,7 @@ export class LedgerReconciliationService {
}

const state = await job.getState();
const progress = job.progress as any;
const progress = job.progress as ReconciliationProgressSnapshot | undefined;

return {
jobId: job.id || 'unknown',
Expand Down
14 changes: 7 additions & 7 deletions app/backend/src/onchain/onchain.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface InitEscrowResult {
transactionHash: string;
timestamp: Date;
status: 'success' | 'failed';
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}

export interface CreateAidPackageParams {
Expand All @@ -45,7 +45,7 @@ export interface CreateAidPackageResult {
transactionHash: string;
timestamp: Date;
status: 'success' | 'failed';
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}

export interface BatchCreateAidPackagesParams {
Expand All @@ -61,7 +61,7 @@ export interface BatchCreateAidPackagesResult {
transactionHash: string;
timestamp: Date;
status: 'success' | 'failed';
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}

export interface ClaimAidPackageParams {
Expand All @@ -75,7 +75,7 @@ export interface ClaimAidPackageResult {
timestamp: Date;
status: 'success' | 'failed';
amountClaimed: string;
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}

export interface DisburseAidPackageParams {
Expand All @@ -89,7 +89,7 @@ export interface DisburseAidPackageResult {
timestamp: Date;
status: 'success' | 'failed';
amountDisbursed: string;
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}

export interface GetAidPackageParams {
Expand Down Expand Up @@ -184,7 +184,7 @@ export interface CreateClaimResult {
transactionHash: string;
timestamp: Date;
status: 'success' | 'failed';
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}

export interface DisburseParams {
Expand All @@ -200,7 +200,7 @@ export interface DisburseResult {
timestamp: Date;
status: 'success' | 'failed';
amountDisbursed: string;
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}

/**
Expand Down
Loading
Loading