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
136 changes: 136 additions & 0 deletions backend/src/migrations/1775000000000-AlignTransactionsEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AlignTransactionsEntity1775000000000
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'transactions_type_enum') THEN
CREATE TYPE "transactions_type_enum" AS ENUM ('DEPOSIT', 'WITHDRAW', 'SWAP', 'YIELD');
END IF;
END
$$;
`);

await queryRunner.query(`
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'transactions' AND column_name = 'type'
) THEN
ALTER TABLE "transactions"
ALTER COLUMN "type" TYPE "transactions_type_enum"
USING (
CASE
WHEN "type" IN ('DEPOSIT', 'WITHDRAW', 'SWAP', 'YIELD') THEN "type"
ELSE 'YIELD'
END
)::"transactions_type_enum";
END IF;
END
$$;
`);

await queryRunner.query(`
ALTER TABLE "transactions"
ALTER COLUMN "amount" TYPE DECIMAL(18,7);
`);

await queryRunner.query(`
ALTER TABLE "transactions"
ADD COLUMN IF NOT EXISTS "txHash" varchar;
`);

await queryRunner.query(`
UPDATE "transactions"
SET "txHash" = COALESCE("transactionHash", "eventId", "id"::text)
WHERE "txHash" IS NULL;
`);

await queryRunner.query(`
ALTER TABLE "transactions"
ALTER COLUMN "txHash" SET NOT NULL;
`);

await queryRunner.query(`
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'UQ_transactions_txHash'
) THEN
ALTER TABLE "transactions"
ADD CONSTRAINT "UQ_transactions_txHash" UNIQUE ("txHash");
END IF;
END
$$;
`);

await queryRunner.query(`
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'transactions_status_enum') THEN
CREATE TYPE "transactions_status_enum" AS ENUM ('COMPLETED', 'PENDING', 'FAILED');
END IF;
END
$$;
`);

await queryRunner.query(`
ALTER TABLE "transactions"
ADD COLUMN IF NOT EXISTS "status" "transactions_status_enum" NOT NULL DEFAULT 'COMPLETED';
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "transactions"
DROP COLUMN IF EXISTS "status";
`);

await queryRunner.query(`
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'transactions_status_enum') THEN
DROP TYPE "transactions_status_enum";
END IF;
END
$$;
`);

await queryRunner.query(`
ALTER TABLE "transactions"
DROP CONSTRAINT IF EXISTS "UQ_transactions_txHash";
`);

await queryRunner.query(`
ALTER TABLE "transactions"
DROP COLUMN IF EXISTS "txHash";
`);

await queryRunner.query(`
ALTER TABLE "transactions"
ALTER COLUMN "amount" TYPE DECIMAL(20,7);
`);

await queryRunner.query(`
ALTER TABLE "transactions"
ALTER COLUMN "type" TYPE varchar USING "type"::text;
`);

await queryRunner.query(`
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_type WHERE typname = 'transactions_type_enum') THEN
DROP TYPE "transactions_type_enum";
END IF;
END
$$;
`);
}
}
56 changes: 5 additions & 51 deletions backend/src/modules/blockchain/entities/transaction.entity.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,5 @@
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
Index,
} from 'typeorm';

export enum LedgerTransactionType {
DEPOSIT = 'DEPOSIT',
WITHDRAW = 'WITHDRAW',
YIELD = 'YIELD',
}

@Entity('transactions')
export class LedgerTransaction {
@PrimaryGeneratedColumn('uuid')
id: string;

@Index('idx_transactions_user_id')
@Column('uuid')
userId: string;

@Column({ type: 'enum', enum: LedgerTransactionType })
type: LedgerTransactionType;

@Column('decimal', { precision: 20, scale: 7 })
amount: string;

@Column({ type: 'varchar', nullable: true })
publicKey: string | null;

@Index('idx_transactions_event_id', { unique: true })
@Column({ type: 'varchar' })
eventId: string;

@Column({ type: 'varchar', nullable: true })
transactionHash: string | null;

@Column({ type: 'bigint', nullable: true })
ledgerSequence: string | null;

@Column({ type: 'varchar', nullable: true })
poolId: string | null;

@Column({ type: 'jsonb', nullable: true })
metadata: Record<string, unknown> | null;

@CreateDateColumn()
createdAt: Date;
}
export {
Transaction as LedgerTransaction,
TxType as LedgerTransactionType,
TxStatus as LedgerTransactionStatus,
} from '../../transactions/entities/transaction.entity';
79 changes: 79 additions & 0 deletions backend/src/modules/transactions/entities/transaction.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
Index,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
import { User } from '../../user/entities/user.entity';

export enum TxType {
DEPOSIT = 'DEPOSIT',
WITHDRAW = 'WITHDRAW',
SWAP = 'SWAP',
YIELD = 'YIELD',
}

export enum TxStatus {
COMPLETED = 'COMPLETED',
PENDING = 'PENDING',
FAILED = 'FAILED',
}

@Entity('transactions')
@Unique(['txHash'])
export class Transaction extends BaseEntity {
@PrimaryGeneratedColumn('uuid')
id: string;

@ManyToOne(() => User, { nullable: false, onDelete: 'CASCADE' })
@JoinColumn({ name: 'userId' })
user?: User;

@Index('idx_transactions_user_id')
@Column('uuid')
userId: string;

@Column({ type: 'enum', enum: TxType })
type: TxType;

@Column('decimal', { precision: 18, scale: 7 })
amount: string;

@Column({ type: 'varchar' })
txHash?: string | null;

@Column({ type: 'enum', enum: TxStatus, default: TxStatus.COMPLETED })
status?: TxStatus;

@Column({ type: 'varchar', nullable: true })
publicKey: string | null;

@Index('idx_transactions_event_id', { unique: true })
@Column({ type: 'varchar', nullable: true })
eventId: string | null;

@Column({ type: 'bigint', nullable: true })
ledgerSequence: string | null;

@Column({ type: 'varchar', nullable: true })
poolId: string | null;

@Column({ type: 'jsonb', nullable: true })
metadata: Record<string, unknown> | null;

@CreateDateColumn()
createdAt: Date;

get transactionHash(): string | null | undefined {
return this.txHash;
}

set transactionHash(value: string | null | undefined) {
this.txHash = value;
}
}
7 changes: 5 additions & 2 deletions backend/src/modules/transactions/transactions.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('TransactionsService', () => {

describe('findAllForUser', () => {
const userId = 'test-user-id';
const mockTransactions: LedgerTransaction[] = [
const mockTransactions: Partial<LedgerTransaction>[] = [
{
id: '1',
userId,
Expand All @@ -74,7 +74,10 @@ describe('TransactionsService', () => {
order: Order.DESC,
});

mockQueryBuilder.getManyAndCount.mockResolvedValue([mockTransactions, 1]);
mockQueryBuilder.getManyAndCount.mockResolvedValue([
mockTransactions as LedgerTransaction[],
1,
]);

const result = await service.findAllForUser(userId, queryDto);

Expand Down
Loading