Skip to content
Merged
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
15 changes: 15 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ import { EmergencyWithdrawal } from './exchange/entities/emergency-withdrawal.en
import { ExchangeModule } from './exchange/exchange.module';
import { MobileModule } from './mobile/mobile.module';

// Protection Domain β€” Insurance Fund (issue #380)
import { InsuranceFund } from './protection/entities/insurance-fund.entity';
import { InsuranceFundTier } from './protection/entities/insurance-fund-tier.entity';
import { InsuranceTransaction } from './protection/entities/insurance-transaction.entity';
import { LiquidationEvent } from './protection/entities/liquidation-event.entity';
import { ProtectionModule } from './protection/protection.module';

@Module({
imports: [
// ── Core NestJS ──
Expand Down Expand Up @@ -148,6 +155,11 @@ import { MobileModule } from './mobile/mobile.module';
GovernanceExecution,
GovernanceConfig,
TokenHolding,
// Protection β€” Insurance Fund
InsuranceFund,
InsuranceFundTier,
InsuranceTransaction,
LiquidationEvent,
],
synchronize: configService.get<boolean>('DB_SYNCHRONIZE', true),
logging: configService.get<boolean>('DB_LOGGING', false),
Expand All @@ -166,6 +178,9 @@ import { MobileModule } from './mobile/mobile.module';
// ── Phase 3: Exchange Domain (DeFi Integration) ──
ExchangeModule,

// ── Protection Domain β€” Insurance Fund (issue #380) ──
ProtectionModule,

// ── Trading Features β€” Advanced Order Types (issue #382) ──
OrdersModule,

Expand Down
3 changes: 3 additions & 0 deletions src/config/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ export const configSchema = Joi.object({
FEATURE_AB_TESTING: Joi.boolean().default(false),
FEATURE_PERFORMANCE_MONITORING: Joi.boolean().default(true),
FEATURE_ERROR_TRACKING: Joi.boolean().default(true),
FEATURE_INSURANCE_ENABLED: Joi.boolean().default(true),
INSURANCE_MIN_RESERVE_PCT: Joi.number().min(0).max(100).default(20),
INSURANCE_FEE_CONTRIBUTION_PCT: Joi.number().min(0).max(100).default(10),

// Vault/Secrets
VAULT_URL: Joi.string().uri().optional(),
Expand Down
245 changes: 245 additions & 0 deletions src/database/migrations/1727516400003-CreateInsuranceFundTables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import { MigrationInterface, QueryRunner, Table } from 'typeorm';

export class CreateInsuranceFundTables1727516400003
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'insurance_fund_tiers',
columns: [
{
name: 'id',
type: 'integer',
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment',
},
{ name: 'tier', type: 'varchar', isUnique: true },
{ name: 'name', type: 'varchar' },
{
name: 'minReserve',
type: 'decimal',
precision: 18,
scale: 8,
default: 0,
},
{
name: 'maxExposure',
type: 'decimal',
precision: 18,
scale: 8,
default: 0,
},
{ name: 'feeContributionPct', type: 'integer', default: 10 },
{ name: 'isActive', type: 'boolean', default: true },
{
name: 'createdAt',
type: 'datetime',
default: 'CURRENT_TIMESTAMP',
},
{
name: 'updatedAt',
type: 'datetime',
default: 'CURRENT_TIMESTAMP',
},
],
indices: [
{
name: 'IDX_insurance_fund_tiers_tier',
columnNames: ['tier'],
isUnique: true,
},
],
}),
);

await queryRunner.createTable(
new Table({
name: 'insurance_funds',
columns: [
{
name: 'id',
type: 'integer',
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment',
},
{ name: 'tierId', type: 'integer' },
{ name: 'asset', type: 'varchar' },
{
name: 'balance',
type: 'decimal',
precision: 18,
scale: 8,
default: 0,
},
{
name: 'targetReserve',
type: 'decimal',
precision: 18,
scale: 8,
default: 0,
},
{ name: 'healthStatus', type: 'varchar', default: "'HEALTHY'" },
{
name: 'healthPercent',
type: 'decimal',
precision: 5,
scale: 2,
default: 100,
},
{ name: 'isActive', type: 'boolean', default: true },
{
name: 'createdAt',
type: 'datetime',
default: 'CURRENT_TIMESTAMP',
},
{
name: 'updatedAt',
type: 'datetime',
default: 'CURRENT_TIMESTAMP',
},
],
foreignKeys: [
{
columnNames: ['tierId'],
referencedTableName: 'insurance_fund_tiers',
referencedColumnNames: ['id'],
onDelete: 'CASCADE',
},
],
indices: [
{ name: 'IDX_insurance_funds_tierId', columnNames: ['tierId'] },
{
name: 'IDX_insurance_funds_tier_asset',
columnNames: ['tierId', 'asset'],
isUnique: true,
},
],
}),
);

await queryRunner.createTable(
new Table({
name: 'insurance_transactions',
columns: [
{
name: 'id',
type: 'varchar',
isPrimary: true,
isGenerated: true,
generationStrategy: 'uuid',
},
{ name: 'fundId', type: 'integer' },
{ name: 'type', type: 'varchar' },
{
name: 'amount',
type: 'decimal',
precision: 18,
scale: 8,
},
{
name: 'balanceBefore',
type: 'decimal',
precision: 18,
scale: 8,
},
{
name: 'balanceAfter',
type: 'decimal',
precision: 18,
scale: 8,
},
{ name: 'referenceId', type: 'varchar', isNullable: true },
{ name: 'userId', type: 'integer', isNullable: true },
{ name: 'description', type: 'text', isNullable: true },
{ name: 'metadata', type: 'text', isNullable: true },
{
name: 'createdAt',
type: 'datetime',
default: 'CURRENT_TIMESTAMP',
},
],
foreignKeys: [
{
columnNames: ['fundId'],
referencedTableName: 'insurance_funds',
referencedColumnNames: ['id'],
onDelete: 'CASCADE',
},
],
indices: [
{
name: 'IDX_insurance_transactions_fundId',
columnNames: ['fundId'],
},
{
name: 'IDX_insurance_transactions_referenceId',
columnNames: ['referenceId'],
},
],
}),
);

await queryRunner.createTable(
new Table({
name: 'liquidation_events',
columns: [
{
name: 'id',
type: 'varchar',
isPrimary: true,
isGenerated: true,
generationStrategy: 'uuid',
},
{ name: 'userId', type: 'integer' },
{ name: 'positionId', type: 'varchar', isNullable: true },
{
name: 'shortfallAmount',
type: 'decimal',
precision: 18,
scale: 8,
},
{
name: 'coveredAmount',
type: 'decimal',
precision: 18,
scale: 8,
default: 0,
},
{ name: 'fundId', type: 'integer', isNullable: true },
{ name: 'cascadePrevented', type: 'boolean', default: false },
{ name: 'status', type: 'varchar', default: "'COMPLETED'" },
{ name: 'notes', type: 'text', isNullable: true },
{
name: 'createdAt',
type: 'datetime',
default: 'CURRENT_TIMESTAMP',
},
],
foreignKeys: [
{
columnNames: ['fundId'],
referencedTableName: 'insurance_funds',
referencedColumnNames: ['id'],
onDelete: 'SET NULL',
},
],
indices: [
{
name: 'IDX_liquidation_events_userId',
columnNames: ['userId'],
},
],
}),
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('liquidation_events');
await queryRunner.dropTable('insurance_transactions');
await queryRunner.dropTable('insurance_funds');
await queryRunner.dropTable('insurance_fund_tiers');
}
}
8 changes: 8 additions & 0 deletions src/identity/permissions/constants/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export const POOLS_READ = 'pools.read';
export const POOLS_WRITE = 'pools.write';
export const LIQUIDITY_MANAGE = 'liquidity.manage';

// Insurance fund permissions
export const INSURANCE_READ = 'insurance.read';
export const INSURANCE_WRITE = 'insurance.write';
export const INSURANCE_ADMIN = 'insurance.admin';

/**
* All available permissions in the system
*/
Expand All @@ -64,4 +69,7 @@ export const ALL_PERMISSIONS = [
POOLS_READ,
POOLS_WRITE,
LIQUIDITY_MANAGE,
INSURANCE_READ,
INSURANCE_WRITE,
INSURANCE_ADMIN,
];
30 changes: 30 additions & 0 deletions src/infrastructure/events/domain.events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,33 @@ export class SwapSettledEvent {
public finalAmount: number,
) {}
}

// Protection / Insurance Domain Events
export class LiquidationShortfallEvent {
constructor(
public liquidationId: string,
public userId: number,
public shortfallAmount: number,
public coveredAmount: number,
public cascadePrevented: boolean,
) {}
}

export class InsurancePayoutEvent {
constructor(
public liquidationId: string,
public userId: number,
public amount: number,
public fundsUsed: Array<{ fundId: number; amount: number; tier: string }>,
) {}
}

export class FundHealthAlertEvent {
constructor(
public fundId: number,
public tier: string,
public healthPercent: number,
public balance: number,
public targetReserve: number,
) {}
}
18 changes: 18 additions & 0 deletions src/protection/dto/contribute-fees.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { IsNumber, IsOptional, IsString, Min } from 'class-validator';
import { FundTier } from '../enums/fund-tier.enum';

export class ContributeFeesDto {
@IsString()
tradeId: string;

@IsNumber()
@Min(0)
feeAmount: number;

@IsOptional()
@IsString()
asset?: string;

@IsOptional()
tier?: FundTier;
}
22 changes: 22 additions & 0 deletions src/protection/dto/cover-shortfall.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { IsInt, IsNumber, IsOptional, IsString, Min } from 'class-validator';
import { FundTier } from '../enums/fund-tier.enum';

export class CoverShortfallDto {
@IsInt()
userId: number;

@IsNumber()
@Min(0)
shortfallAmount: number;

@IsOptional()
@IsString()
positionId?: string;

@IsOptional()
@IsString()
asset?: string;

@IsOptional()
tier?: FundTier;
}
Loading
Loading