File

src/orderbook/settlement.service.ts

Index

Properties
Methods

Constructor

constructor(settlementModel: Model<SettlementDocument>, allowanceModel: Model<AllowanceDocument>)
Parameters :
Name Type Optional
settlementModel Model<SettlementDocument> No
allowanceModel Model<AllowanceDocument> No

Methods

Async approveSettlement
approveSettlement(undefined: IApproveSettlement)
Parameters :
Name Type Optional
IApproveSettlement No
Returns : Promise<literal type>
Async authenticateFundAdmin
authenticateFundAdmin(userJwt: UserJwtPayload, fundId: string)
Parameters :
Name Type Optional
userJwt UserJwtPayload No
fundId string No
Returns : any
Async create
create(createSettlementDto)
Parameters :
Name Optional
createSettlementDto No
Returns : unknown
Async getAllSettlements
getAllSettlements(undefined)
Parameters :
Name Optional
No
Returns : unknown
Async getSettlementByOrderBookId
getSettlementByOrderBookId(undefined: IGetSettlementApproval)
Parameters :
Name Type Optional
IGetSettlementApproval No
Returns : unknown
Async resetVote
resetVote(orderbookId: string)
Parameters :
Name Type Optional
orderbookId string No
Returns : unknown

Properties

Private Readonly userService
Type : UserService
Decorators :
@Inject(UserService)
import { UserJwtPayload } from "./../common/interfaces/SharedTypes";
import {
  BadRequestException,
  Inject,
  Injectable,
  ServiceUnavailableException,
  UnauthorizedException,
} from "@nestjs/common";

import { UserService } from "src/user/user.service";
import { Settlement } from "./entities/settlement.entity";
import { InjectModel } from "@nestjs/mongoose";
import { Model } from "mongoose";
import { SettlementDocument } from "./schemas/settlement.schema";
import { CreateSettlementDto } from "./dto/create-settlement.dto";
import {
  IApproveSettlement,
  IGetSettlementApproval,
} from "./orderbook.interface";
import { decodeBytes32String, encodeBytes32String } from "ethers";
import { Allowance } from "./entities/allowance.entity";
import { AllowanceDocument } from "./schemas/allowance.schema";
import { FilterPaginationDto } from "src/shared/dto/filter-pagination.dto";
import { ApolloQueryResult } from "@apollo/client";
import { IGetOrderBooksByFundIdsQuery } from "src/common/interfaces";
import { apolloClient, getOrderBooksByFundIdsQuery } from "src/common/provider";

@Injectable()
export class SettlementService {
  @Inject(UserService)
  private readonly userService: UserService;

  constructor(
    @InjectModel(Settlement.name)
    private readonly settlementModel: Model<SettlementDocument>,
    @InjectModel(Allowance.name)
    private readonly allowanceModel: Model<AllowanceDocument>,
  ) {}

  async create(
    createSettlementDto: CreateSettlementDto & { orderbookId: string },
  ) {
    return this.settlementModel.create({
      ...createSettlementDto,
      currentApprovalNo: 0,
    });
  }

  async resetVote(orderbookId: string) {
    return this.settlementModel.findOneAndUpdate(
      { orderbookId },
      {
        currentApprovalNo: 0,
      },
    );
  }
  async getAllSettlements({
    userJwt,
    skip,
    limit,
    orderDirection,
  }: FilterPaginationDto & { userJwt: UserJwtPayload }) {
    if (userJwt.role === "admin") {
      return await this.settlementModel
        .find()
        .limit(limit)
        .skip(skip * limit)
        .sort({ created: orderDirection })
        .lean()
        .exec();
    }
    const user = await this.userService.findUserByProperty({
      email: userJwt.email,
    });
    const encodedFundsId = user.subscribedFunds.map((fund) =>
      encodeBytes32String(fund._id.toString()),
    );
    const orderBooks: ApolloQueryResult<IGetOrderBooksByFundIdsQuery> =
      await apolloClient.query({
        query: getOrderBooksByFundIdsQuery,
        variables: {
          orderBookId: encodedFundsId,
        },
      });
    if (orderBooks.errors) {
      console.error(orderBooks.errors[0]);
      throw new ServiceUnavailableException(orderBooks.error);
    }

    if (!orderBooks.data.getOrderBooksByFundIdsQuery.length) {
      return [];
    }
    return await this.settlementModel
      .find({
        orderbookId: {
          $in: orderBooks.data.getOrderBooksByFundIdsQuery.map(
            (orderBook) => orderBook.id,
          ),
        },
      })
      .limit(limit)
      .skip(skip * limit)
      .sort({ created: orderDirection })
      .lean()
      .exec();
  }
  async getSettlementByOrderBookId({
    orderbookId,
    fundId,
    userJwt,
  }: IGetSettlementApproval) {
    if (userJwt.role === "admin") {
      return await this.settlementModel.findOne({ orderbookId }).lean().exec();
    }
    await this.authenticateFundAdmin(userJwt, fundId);
    return await this.settlementModel.findOne({ orderbookId }).lean().exec();
  }

  async approveSettlement({
    orderbookId,
    fundId,
    userJwt,
    orderId,
  }: IApproveSettlement): Promise<{
    isAllowed: boolean;
    requiredApprovalNo: number;
    currentApprovalNo: number;
  }> {
    const [, settlement, isAlreadyVoted] = await Promise.all([
      this.authenticateFundAdmin(userJwt, fundId),
      this.settlementModel.findOne({ orderbookId }).lean().exec(),
      this.allowanceModel.findOne({
        orderbookId,
        fundId,
        userEmail: userJwt.email,
        orderId,
      }),
    ]);
    if (isAlreadyVoted) {
      throw new BadRequestException("User already voted");
    }
    if (settlement.requiredApprovalNo <= settlement.currentApprovalNo) {
      return {
        isAllowed: true,
        requiredApprovalNo: settlement.requiredApprovalNo,
        currentApprovalNo: settlement.currentApprovalNo,
      };
    }
    const updatedSettlement = await this.settlementModel
      .findOneAndUpdate(
        { orderbookId },
        { $inc: { currentApprovalNo: 1 } },
        { new: true },
      )
      .exec();
    await this.allowanceModel.create({
      orderbookId,
      fundId,
      userEmail: userJwt.email,
      orderId,
    });
    return {
      isAllowed:
        settlement.requiredApprovalNo <= updatedSettlement.currentApprovalNo,
      requiredApprovalNo: settlement.requiredApprovalNo,
      currentApprovalNo: updatedSettlement.currentApprovalNo,
    };
  }

  async authenticateFundAdmin(userJwt: UserJwtPayload, fundId: string) {
    const fundAdmin = await this.userService.findUserByProperty({
      email: userJwt.email,
    });
    const decodedFundId = decodeBytes32String(fundId);
    const isSubscribed = fundAdmin.subscribedFunds.map(
      (fund) => fund._id.toString() === decodedFundId,
    );
    if (!isSubscribed) {
      throw new UnauthorizedException(
        "Fund admin is not subscribed to this fund",
      );
    }
  }
}

results matching ""

    No results matching ""