src/portfolio/portfolio.service.ts
Properties |
|
Methods |
|
| Private Async generatePasteSettlementsInfo | |||||||||
generatePasteSettlementsInfo(settledOrdersGraph: ApolloQueryResult<IGetAllSettledOrdersQuery>, funds: FundWithInstrument[])
|
|||||||||
|
Defined in src/portfolio/portfolio.service.ts:155
|
|||||||||
|
Parameters :
Returns :
Promise<IPastSettlement[]>
|
| Private Async getAdminPastSettlements | |||||||||
getAdminPastSettlements(user: UserJwtPayload, filterPaginationDto?: FilterPaginationDto)
|
|||||||||
|
Defined in src/portfolio/portfolio.service.ts:100
|
|||||||||
|
Parameters :
Returns :
Promise<IPastSettlement[]>
|
| Public Async getCurrentSettlements | |||||||||
getCurrentSettlements(user: UserJwtPayload, filterPaginationDto?: FilterPaginationDto)
|
|||||||||
|
Defined in src/portfolio/portfolio.service.ts:231
|
|||||||||
|
Parameters :
Returns :
Promise<ICurrentHolding[]>
|
| Public Async getPasteSettlements | |||||||||
getPasteSettlements(user: UserJwtPayload, filterPaginationDto?: FilterPaginationDto)
|
|||||||||
|
Defined in src/portfolio/portfolio.service.ts:46
|
|||||||||
|
Parameters :
Returns :
Promise<IPastSettlement[]>
|
| Private Async getUserPastSettlements | |||||||||
getUserPastSettlements(user: UserJwtPayload, filterPaginationDto?: FilterPaginationDto)
|
|||||||||
|
Defined in src/portfolio/portfolio.service.ts:58
|
|||||||||
|
Parameters :
Returns :
Promise<IPastSettlement[]>
|
| Private Readonly fundService |
Type : FundService
|
Decorators :
@Inject(FundService)
|
|
Defined in src/portfolio/portfolio.service.ts:42
|
| Private Readonly userService |
Type : UserService
|
Decorators :
@Inject(UserService)
|
|
Defined in src/portfolio/portfolio.service.ts:44
|
import { ApolloQueryResult } from "@apollo/client";
import {
Inject,
Injectable,
ServiceUnavailableException,
} from "@nestjs/common";
import {
decodeBytes32String,
encodeBytes32String,
formatEther,
formatUnits,
} from "ethers";
import {
EndUserRoles,
IGetAllSettledOrdersQuery,
IGetLatestNavsByInstrumentsIdsQuery,
INavQuery,
UserJwtPayload,
} from "src/common/interfaces";
import {
apolloClient,
getAllAdminSettledOrdersQuery,
getAllDealerSettledOrdersQuery,
getAllFundAdminSettledOrdersQuery,
getAllInvestorSettledOrdersQuery,
getLatestNavsByInstrumentsIdsQuery,
} from "src/common/provider";
import {
FundWithInstrument,
InstrumentSubgraphAndDB,
} from "src/security/fund.interface";
import { FundService } from "src/security/fund.service";
import { User } from "src/user/schemas/user.schema";
import { UserService } from "src/user/user.service";
import { IPastSettlement, ICurrentHolding } from "./portfolio.interface";
import { DEFAULT_USER_ADDRESS } from "src/common/constants";
import { FilterPaginationDto } from "src/shared/dto/filter-pagination.dto";
@Injectable()
export class PortfolioService {
@Inject(FundService)
private readonly fundService: FundService;
@Inject(UserService)
private readonly userService: UserService;
public async getPasteSettlements(
user: UserJwtPayload,
filterPaginationDto?: FilterPaginationDto,
): Promise<IPastSettlement[]> {
if (
user.role === EndUserRoles.dealer ||
user.role === EndUserRoles.investor
) {
return await this.getUserPastSettlements(user, filterPaginationDto);
}
return await this.getAdminPastSettlements(user, filterPaginationDto);
}
private async getUserPastSettlements(
user: UserJwtPayload,
filterPaginationDto?: FilterPaginationDto,
): Promise<IPastSettlement[]> {
try {
const userDb = await this.userService.findUserByProperty({
email: user.email,
...filterPaginationDto,
});
const fundsId = userDb.subscribedFunds.map((subscribedFund) =>
subscribedFund.toString(),
);
const { limit, skip, orderDirection } = filterPaginationDto ?? {};
const [settledOrdersGraph, funds]: [
ApolloQueryResult<IGetAllSettledOrdersQuery>,
FundWithInstrument[],
] = await Promise.all([
apolloClient.query({
query:
user.role === EndUserRoles.dealer
? getAllDealerSettledOrdersQuery
: getAllInvestorSettledOrdersQuery,
variables: {
userId: encodeBytes32String(userDb._id.toString()),
fundsId: fundsId.map((fundId) =>
encodeBytes32String(fundId.toString()),
),
first: limit,
skip,
orderDirection,
},
}),
this.fundService.findByIdsAndInstrument({ ids: fundsId }),
]);
return this.generatePasteSettlementsInfo(settledOrdersGraph, funds);
} catch (error) {
console.error(error);
throw new ServiceUnavailableException(error);
}
}
private async getAdminPastSettlements(
user: UserJwtPayload,
filterPaginationDto?: FilterPaginationDto,
): Promise<IPastSettlement[]> {
try {
const { limit, skip, orderDirection } = filterPaginationDto ?? {};
if (user.role === "admin") {
const [settledOrdersGraph, funds]: [
ApolloQueryResult<IGetAllSettledOrdersQuery>,
FundWithInstrument[],
] = await Promise.all([
apolloClient.query({
query: getAllAdminSettledOrdersQuery,
variables: { first: limit, skip: skip * limit, orderDirection },
}),
this.fundService.findFundsByFilter(filterPaginationDto) as Promise<
FundWithInstrument[]
>,
]);
return this.generatePasteSettlementsInfo(settledOrdersGraph, funds);
}
const fundAdmin = await this.userService.findUserByProperty({
email: user.email,
});
const fundsId = fundAdmin.subscribedFunds.map((subscribedFund) =>
subscribedFund.toString(),
);
const [settledOrdersGraph, funds]: [
ApolloQueryResult<IGetAllSettledOrdersQuery>,
FundWithInstrument[],
] = await Promise.all([
apolloClient.query({
query: getAllFundAdminSettledOrdersQuery,
variables: {
fundsId: fundsId.map((fundId) =>
encodeBytes32String(fundId.toString()),
),
first: limit,
skip,
orderDirection,
},
}),
this.fundService.findByIdsAndInstrument({
ids: fundsId,
}),
]);
return await this.generatePasteSettlementsInfo(settledOrdersGraph, funds);
} catch (error) {
console.error(error);
throw new ServiceUnavailableException(error);
}
}
private async generatePasteSettlementsInfo(
settledOrdersGraph: ApolloQueryResult<IGetAllSettledOrdersQuery>,
funds: FundWithInstrument[],
): Promise<IPastSettlement[]> {
try {
const fundByEncodedId: Record<string, FundWithInstrument> = {};
const instrumentByEncodedId: Record<string, InstrumentSubgraphAndDB> = {};
const investorById: Record<string, User> = {};
const investorIds: string[] = [];
settledOrdersGraph.data.orders.forEach((order) => {
investorIds.push(decodeBytes32String(order.onBehalf.id));
});
const investors = await this.userService.findByIds(investorIds);
investors.forEach((investor) => {
investorById[investor._id.toString()] = investor;
});
funds.forEach((fund) => {
fundByEncodedId[encodeBytes32String(fund._id.toString())] = fund;
fund.instruments.forEach((instrument) => {
instrumentByEncodedId[instrument.subgraphInstrument.id] = instrument;
});
});
const settledOrders: IPastSettlement[] = [];
settledOrdersGraph.data.orders.forEach((order) => {
const decodedInvestorId = decodeBytes32String(order.onBehalf.id);
const settlementInfo = {
name: investorById[decodedInvestorId].name,
address:
investorById[decodedInvestorId].address || DEFAULT_USER_ADDRESS,
investorId: decodedInvestorId,
fund: fundByEncodedId[order.orderBook.fund.id].title,
fundId: fundByEncodedId[order.orderBook.fund.id]._id,
shareClass:
instrumentByEncodedId[order.orderBook.instrument.id].dbInstrument
.name,
shareId:
instrumentByEncodedId[
order.orderBook.instrument.id
].dbInstrument._id.toString(),
settlementType: order.orderBook.type,
};
if (order.type === "Subscription") {
settledOrders.push({
...settlementInfo,
shares: `${
Number(formatUnits(order.amount, 6)) *
Number(formatUnits(order.settledOnNavPerShare, 6))
}`,
navPerShare: formatUnits(order.settledOnNavPerShare, 6),
settlementDate: order.settleTime,
amountInFiat: formatUnits(order.amount, 6),
settlementHash: order.settledTxHash,
});
} else {
settledOrders.push({
...settlementInfo,
shares: formatEther(order.amount),
settlementDate: order.settleTime,
navPerShare: formatUnits(order.settledOnNavPerShare, 6),
amountInFiat: `${
Number(formatEther(order.amount)) *
Number(formatUnits(order.settledOnNavPerShare, 6))
}`,
settlementHash: order.settledTxHash,
});
}
});
return settledOrders;
} catch (error) {
console.error(error);
throw new ServiceUnavailableException(error);
}
}
public async getCurrentSettlements(
user: UserJwtPayload,
filterPaginationDto?: FilterPaginationDto,
): Promise<ICurrentHolding[]> {
const userPasteSettlements = await this.getPasteSettlements(
user,
filterPaginationDto,
);
const instrumentsIds = Array.from(
new Set(
userPasteSettlements.map((settlement) =>
encodeBytes32String(settlement.shareId),
),
),
);
const latestNavsSubgraph: ApolloQueryResult<IGetLatestNavsByInstrumentsIdsQuery> =
await apolloClient.query({
query: getLatestNavsByInstrumentsIdsQuery,
variables: {
ids: instrumentsIds,
},
});
const navByInstrumentId: Record<string, Partial<INavQuery>> = {};
latestNavsSubgraph.data.instruments.forEach((instrument) => {
navByInstrumentId[decodeBytes32String(instrument.id)] = {
...instrument.navs[0],
navPerShare: formatUnits(instrument.navs[0].navPerShare, 6),
};
});
const groupSettlementsInvestorFundShareId: Record<
string, // investorId-fundId-shareId
ICurrentHolding
> = {};
userPasteSettlements.forEach((settlement) => {
const settlementId = `${settlement.investorId}-${settlement.fundId}-${settlement.shareId}`;
const groupedSettlements =
groupSettlementsInvestorFundShareId[settlementId];
const currentShares =
settlement.settlementType === "Redemption"
? Number(settlement.shares) * -1
: Number(settlement.shares);
if (groupedSettlements) {
const sharesHeld = groupedSettlements.sharesHeld + currentShares;
const currentValue =
groupedSettlements.currentValue +
currentShares *
Number(navByInstrumentId[settlement.shareId].navPerShare);
groupSettlementsInvestorFundShareId[settlementId] = {
...groupedSettlements,
sharesHeld,
currentValue,
settlements: [...groupedSettlements.settlements, settlement],
};
} else {
const { name, address, investorId, fund, shareClass, fundId, shareId } =
settlement;
groupSettlementsInvestorFundShareId[settlementId] = {
name,
address,
investorId,
fund,
shareClass,
fundId,
shareId,
sharesHeld: currentShares,
currentValue:
currentShares *
Number(navByInstrumentId[settlement.shareId].navPerShare),
auditedNav: Number(navByInstrumentId[settlement.shareId].navPerShare),
navDate: navByInstrumentId[settlement.shareId].timestamp,
settlements: [settlement],
};
}
});
return Object.values(groupSettlementsInvestorFundShareId).flat();
}
}