Skip to content
Closed
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
89 changes: 80 additions & 9 deletions src/modules/accounts/accounts.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,54 +12,125 @@ import {
ApiOperation,
ApiResponse,
ApiBearerAuth,
ApiQuery,
} from '@nestjs/swagger';
import { ThrottlerGuard } from '@nestjs/throttler';
import { AccountsService } from './accounts.service.js';
import { CreateAccountDto } from './dto/create-account.dto.js';
import { AccountResponseDto } from './dto/account-response.dto.js';
import { AccountsListResponseDto } from './dto/accounts-list-response.dto.js';
import { AccountStatus } from './entities/account.entity.js';

@ApiTags('accounts')
@ApiBearerAuth()
@Controller('accounts')
@UseGuards(ThrottlerGuard)
export class AccountsController {
constructor(private readonly accountsService: AccountsService) {}

@Post()
@ApiOperation({ summary: 'Create ephemeral account' })
@ApiOperation({
summary: 'Create ephemeral escrow account',
description:
'Creates a temporary Stellar escrow account for fund holding. ' +
'This is NOT a user wallet — it is an ephemeral account that will be ' +
'swept to a destination address upon claim or expire after the set duration. ' +
'Returns claim URL for the recipient to access funds.',
})
@ApiResponse({
status: 201,
description: 'Account created',
description: 'Ephemeral account created successfully',
type: AccountResponseDto,
})
@ApiResponse({ status: 400, description: 'Invalid input' })
@ApiBearerAuth()
@ApiResponse({ status: 400, description: 'Invalid input parameters' })
@ApiResponse({ status: 401, description: 'Authentication required' })
@ApiResponse({
status: 429,
description:
'Rate limit exceeded — requests are throttled to protect funding flows',
})
public async create(
@Body() createAccountDto: CreateAccountDto,
): Promise<AccountResponseDto> {
return this.accountsService.create(createAccountDto);
}

@Get(':id')
@ApiOperation({ summary: 'Get account by ID' })
@ApiOperation({
summary: 'Get ephemeral account lifecycle state',
description:
'Retrieves the current state of an ephemeral account including its ' +
'status (pending_payment, pending_claim, claimed, expired, failed), ' +
'funding details, and claim information.',
})
@ApiResponse({
status: 200,
description: 'Account details',
description: 'Account details retrieved successfully',
type: AccountResponseDto,
})
@ApiResponse({ status: 401, description: 'Authentication required' })
@ApiResponse({ status: 404, description: 'Account not found' })
@ApiResponse({
status: 429,
description:
'Rate limit exceeded — requests are throttled to protect funding flows',
})
public async findOne(@Param('id') id: string): Promise<AccountResponseDto> {
return this.accountsService.findOne(id);
}

@Get()
@ApiOperation({ summary: 'List accounts' })
@ApiResponse({ status: 200, description: 'List of accounts' })
@ApiOperation({
summary: 'List ephemeral accounts (Admin)',
description:
'Administrative endpoint for listing all ephemeral accounts with optional filtering. ' +
'Supports filtering by status and cursor-based pagination using limit/offset. ' +
'Maximum 100 records per request.',
})
@ApiQuery({
name: 'status',
enum: AccountStatus,
required: false,
description:
'Filter accounts by lifecycle status:\n' +
'- `pending_payment`: Account created, awaiting funding\n' +
'- `pending_claim`: Funded and ready for recipient claim\n' +
'- `claimed`: Funds successfully transferred to recipient\n' +
'- `expired`: Claim window closed, funds swept back\n' +
'- `failed`: Error during creation or funding',
})
@ApiQuery({
name: 'limit',
type: Number,
required: false,
description: 'Maximum number of records to return (default: 50, max: 100)',
example: 50,
})
@ApiQuery({
name: 'offset',
type: Number,
required: false,
description:
'Number of records to skip for cursor-based pagination. ' +
'Use with limit to paginate through results. Not page-based.',
example: 0,
})
@ApiResponse({
status: 200,
description: 'List of accounts retrieved successfully',
type: AccountsListResponseDto,
})
@ApiResponse({ status: 401, description: 'Authentication required' })
@ApiResponse({
status: 429,
description:
'Rate limit exceeded — requests are throttled to protect funding flows',
})
public async findAll(
@Query('status') status?: AccountStatus,
@Query('limit') limit = 50,
@Query('offset') offset = 0,
) {
): Promise<AccountsListResponseDto> {
return this.accountsService.findAll({ status, limit, offset });
}
}
15 changes: 1 addition & 14 deletions src/modules/accounts/dto/account-response.dto.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { AccountStatus } from '../entities/account.entity.js';

/**
* Response DTO for account details
*
* Represents the public-facing contract for ephemeral Stellar account information.
* This DTO is used in both account creation responses and account lookup endpoints.
*
* @remarks
* - All fields are readonly to ensure immutability in API responses
* - Date fields are in ISO 8601 format
* - Amounts are represented as string decimals to preserve precision
* - Status enum values indicate the current lifecycle state of the account
*/
export class AccountResponseDto {
@ApiProperty({
description:
Expand Down Expand Up @@ -64,8 +52,7 @@ export class AccountResponseDto {
amount: string;

@ApiProperty({
description:
'Asset code for the funds in this account (e.g., XLM, USDC)',
description: 'Asset code for the funds in this account (e.g., XLM, USDC)',
example: 'XLM',
maxLength: 100,
readOnly: true,
Expand Down
16 changes: 16 additions & 0 deletions src/modules/accounts/dto/accounts-list-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { AccountResponseDto } from './account-response.dto.js';

export class AccountsListResponseDto {
@ApiProperty({
description: 'Array of ephemeral account records',
type: [AccountResponseDto],
})
accounts: AccountResponseDto[];

@ApiProperty({
description: 'Total number of accounts matching the query (for pagination)',
example: 150,
})
total: number;
}
Loading