Skip to content

feature:timed expiry of wallets is implemented in the backend and can be integrated with frontend when needed.#55

Open
0xvikram wants to merge 1 commit intoMXmaster2s:mainfrom
0xvikram:new-feature
Open

feature:timed expiry of wallets is implemented in the backend and can be integrated with frontend when needed.#55
0xvikram wants to merge 1 commit intoMXmaster2s:mainfrom
0xvikram:new-feature

Conversation

@0xvikram
Copy link
Copy Markdown

The flow for the backend

User creates wallet → Sets expiry (e.g., "1 hour")

Frontend sends ttlHours: 1

Controller receives & validates (1-8760 hours allowed)

Service passes to SeedManager

SeedManager calculates: expiresAt = now + (ttlHours * 60 * 60 * 1000)

Repository stores encrypted seed with expiresAt timestamp

I have tested it on the backend, and it is working fine
Screenshot From 2026-01-26 20-19-50

Copilot AI review requested due to automatic review settings January 26, 2026 15:05
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Jan 26, 2026

@0xvikram is attempting to deploy a commit to the Tempwallets Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements timed wallet expiry functionality for the backend, allowing wallets to be automatically deleted after a specified time period. The implementation adds an optional ttlHours parameter to wallet creation (1-8760 hours), stores expiry timestamps in the database, and includes a scheduled cleanup service that runs hourly to remove expired wallets.

Changes:

  • Added @nestjs/schedule dependency and scheduled cleanup service to automatically delete expired wallets hourly
  • Extended database schema with expiresAt field in WalletSeed table and added expiry validation in SeedRepository
  • Added ttlHours parameter (1-8760 hours) to wallet creation API with DTO validation and expiry calculation logic

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
pnpm-lock.yaml Added @nestjs/schedule v6.1.0 and its dependencies (cron, luxon) for scheduled task support
apps/backend/package.json Added @nestjs/schedule dependency to enable cron-based cleanup jobs
apps/backend/prisma/schema.prisma Added expiresAt field to WalletSeed model with index, and walletExpiresAt to User model
apps/backend/prisma/migrations/20260126061639_add_wallet_expiry/migration.sql Database migration adding expiry timestamp columns and index
apps/backend/src/wallet/wallet.module.ts Registered ScheduleModule and WalletCleanupService in the module
apps/backend/src/wallet/services/wallet.cleanup.service.ts New service implementing hourly cron job to delete expired wallets for both registered and unregistered users
apps/backend/src/wallet/seed.repository.ts Updated to store and validate expiry timestamps, throws ForbiddenException for expired wallets
apps/backend/src/wallet/managers/seed.manager.ts Added TTL calculation logic converting hours to milliseconds and creating expiry Date
apps/backend/src/wallet/wallet.service.ts Integrated ttlHours parameter into seed creation flow
apps/backend/src/wallet/wallet.controller.ts Added ttlHours to createOrImportSeed endpoint with enhanced logging
apps/backend/src/wallet/dto/wallet.dto.ts Added ttlHours validation with @min(1) and @max(8760) decorators
apps/web/lib/api.ts Added WALLET_EXPIRY_OPTIONS constant and ttlHours to CreateOrImportSeedRequest interface
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +144 to +163
// Delete entire user record for burner wallets
if (unregisteredUserIds.length > 0) {
const burnerResult = await this.prisma.user.deleteMany({
where: { id: { in: unregisteredUserIds } },
});
deletedBurnerWallets = burnerResult.count;
}

// Delete only WalletSeed for registered users
if (registeredUserIds.length > 0) {
// Clean up cached addresses
await this.prisma.walletAddressCache.deleteMany({
where: { fingerprint: { in: registeredUserIds } },
});

const seedResult = await this.prisma.walletSeed.deleteMany({
where: { userId: { in: registeredUserIds } },
});
deletedRegisteredSeeds = seedResult.count;
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The WalletCache table cleanup is missing in the manual cleanup method. Similar to the scheduled cleanup, both unregistered and registered user cleanups should include deletion of WalletCache records to ensure complete cleanup of expired wallets.

Copilot uses AI. Check for mistakes.
Comment thread apps/web/lib/api.ts
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5005';
// Wallet expiry time options (in hours)
export const WALLET_EXPIRY_OPTIONS = [
{ label: '10 minutes', hours: 10 / 60 },
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The first option has a formatting inconsistency - it uses division (10 / 60) instead of a decimal literal. For consistency with the other options and to avoid potential floating-point precision issues, change this to hours: 0.167 or hours: 1/6.

Suggested change
{ label: '10 minutes', hours: 10 / 60 },
{ label: '10 minutes', hours: 0.167 },

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +30
@IsOptional()
@Min(1)
@Max(8760)
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The validation decorators order should be corrected. The @IsNumber() decorator should come before @IsOptional() for better validation flow. Additionally, @min(1) allows fractional hours like 0.5 (30 minutes) to fail validation, but the frontend WALLET_EXPIRY_OPTIONS includes fractional values like 0.5 and 0.167. Consider changing @min(1) to @min(0.001) or documenting that only whole hours are supported on the backend.

Suggested change
@IsOptional()
@Min(1)
@Max(8760)
@Min(0.001)
@Max(8760)
@IsOptional()

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +4
ALTER TABLE "User" ADD COLUMN "walletExpiresAt" TIMESTAMP(3);

-- AlterTable
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The migration adds the walletExpiresAt column to the User table, but this field is never used in the codebase. The wallet expiry functionality uses the expiresAt field in the WalletSeed table instead. Consider removing this column from both the schema and the migration to avoid confusion and database bloat.

Suggested change
ALTER TABLE "User" ADD COLUMN "walletExpiresAt" TIMESTAMP(3);
-- AlterTable

Copilot uses AI. Check for mistakes.
import { AptosAddressManager } from './aptos/managers/aptos-address.manager.js';
import { AptosAccountFactory } from './aptos/factories/aptos-account.factory.js';
import { AptosRpcService } from './aptos/services/aptos-rpc.service.js';
//imported scheduled call and wallet cleanup service
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The comment formatting is inconsistent with the rest of the file. Other import comments in this file use capital letters and proper punctuation (e.g., "// Import Substrate module"). Change to "// Import scheduled task and wallet cleanup service" for consistency.

Suggested change
//imported scheduled call and wallet cleanup service
// Import scheduled task and wallet cleanup service

Copilot uses AI. Check for mistakes.
xp Int @default(0) // Experience points
createdAt DateTime @default(now())
lastLoginAt DateTime @updatedAt
walletExpiresAt DateTime?
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The walletExpiresAt field is added to the User model but is never used in the codebase. The wallet expiry functionality uses the expiresAt field in the WalletSeed model instead. This field should either be removed or implemented if there's a different intended purpose.

Suggested change
walletExpiresAt DateTime?

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +75
// Step 3a: Delete ENTIRE user record for unregistered burner wallets
// (CASCADE deletes WalletSeed, addresses, etc.)
if (unregisteredUserIds.length > 0) {
const burnerResult = await this.prisma.user.deleteMany({
where: { id: { in: unregisteredUserIds } },
});
deletedBurnerWallets = burnerResult.count;
this.logger.log(
`Deleted ${deletedBurnerWallets} expired burner wallet(s) (entire user records)`,
);
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The WalletCache table (which stores cached balances keyed by fingerprint/userId) is not being cleaned up when users are deleted. When deleting unregistered users, the cleanup service should also delete corresponding WalletCache records to avoid orphaned data. Add a deletion statement similar to the WalletAddressCache cleanup at line 81.

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +92
// Step 3b: For registered users, delete ONLY the WalletSeed
// Keep User record so they can create a new wallet
if (registeredUserIds.length > 0) {
// Also delete cached addresses for these users
await this.prisma.walletAddressCache.deleteMany({
where: { fingerprint: { in: registeredUserIds } },
});

const seedResult = await this.prisma.walletSeed.deleteMany({
where: { userId: { in: registeredUserIds } },
});
deletedRegisteredSeeds = seedResult.count;
this.logger.log(
`Deleted ${deletedRegisteredSeeds} expired seed(s) for registered users (kept user accounts)`,
);
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The WalletCache table cleanup is missing for registered users as well. When deleting expired seeds for registered users, the corresponding WalletCache records should also be deleted to ensure complete cleanup. Add a WalletCache deletion statement before or after the WalletAddressCache deletion.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants