src/user/user.service.ts
Properties |
|
Methods |
|
constructor(userModel: Model<UserDocument>)
|
||||||
|
Defined in src/user/user.service.ts:106
|
||||||
|
Parameters :
|
| Async addNewWallet | |||||
addNewWallet(undefined: IAddedWallet)
|
|||||
|
Defined in src/user/user.service.ts:588
|
|||||
|
Parameters :
Returns :
unknown
|
| Async checkIfUserIsAllowedToFund | |||||||||
checkIfUserIsAllowedToFund(userJwt: UserJwtPayload, fundId: string)
|
|||||||||
|
Defined in src/user/user.service.ts:637
|
|||||||||
|
Parameters :
Returns :
unknown
|
| convertToBatch | ||||||
convertToBatch(setRegistryDataFields: ISetRegistryDataFields)
|
||||||
|
Defined in src/user/user.service.ts:705
|
||||||
|
Parameters :
Returns :
IBatch
|
| Async create | ||||||
create(createUser: UserEntity)
|
||||||
|
Defined in src/user/user.service.ts:114
|
||||||
|
Parameters :
Returns :
Promise<UserDocument>
|
| Async deleteByEmail | ||||||
deleteByEmail(email: string)
|
||||||
|
Defined in src/user/user.service.ts:141
|
||||||
|
Parameters :
Returns :
Promise<void>
|
| Async findByIds | ||||||
findByIds(ids: string[])
|
||||||
|
Defined in src/user/user.service.ts:149
|
||||||
|
Parameters :
Returns :
unknown
|
| Async findOne |
findOne(id: string, hasOnChainFields?: boolean)
|
|
Defined in src/user/user.service.ts:174
|
|
Returns :
Promise<UserWithOnChainFields | User>
|
| Async findUserByProperty | |||||||||
findUserByProperty(filterUserDto: FilterUserDto, hasSubscribedFundsPopulated?: boolean)
|
|||||||||
|
Defined in src/user/user.service.ts:160
|
|||||||||
|
Parameters :
Returns :
Promise<LeanDocument<UserDocument>>
|
| Async findUsersByFilter | |||||||||
findUsersByFilter(filterUserDto: FilterUserDto, includeOnChainField?: boolean)
|
|||||||||
|
Defined in src/user/user.service.ts:193
|
|||||||||
|
Parameters :
Returns :
unknown
|
| Async findUsersByFilterByUsersBalance | |||||||||
findUsersByFilterByUsersBalance(filterUserDto: FilterUserDto, unconfirmedRedemptionAmountById: Record
|
|||||||||
|
Defined in src/user/user.service.ts:330
|
|||||||||
|
Parameters :
Returns :
unknown
|
| Async getCustodial |
getCustodial(email: string, address?: string)
|
|
Defined in src/user/user.service.ts:731
|
|
Returns :
unknown
|
| Async getFundSubscriptionDetails | |||||
getFundSubscriptionDetails(undefined: IGetFundSubscriptionDetails)
|
|||||
|
Defined in src/user/user.service.ts:550
|
|||||
|
Parameters :
Returns :
unknown
|
| Async getInvestorBalanceByInstrument | |||||
getInvestorBalanceByInstrument(undefined: IGetInvestorBalanceByInstrument)
|
|||||
|
Defined in src/user/user.service.ts:743
|
|||||
|
Parameters :
Returns :
unknown
|
| Async getUsersOnChainFields | ||||||
getUsersOnChainFields(users: LeanDocument<UserDocument>[])
|
||||||
|
Defined in src/user/user.service.ts:239
|
||||||
|
Parameters :
Returns :
Promise<UserWithOnChainFields[]>
|
| Async isDealerAllowed | |||||
isDealerAllowed(undefined: IDealerIsAllowed)
|
|||||
|
Defined in src/user/user.service.ts:620
|
|||||
|
Parameters :
Returns :
unknown
|
| Async setRegistryData | ||||||
setRegistryData(setRegistryDataFields: ISetRegistryDataFields)
|
||||||
|
Defined in src/user/user.service.ts:654
|
||||||
|
Parameters :
Returns :
unknown
|
| Async setUsersInstrumentBalance | |||||
setUsersInstrumentBalance(undefined: ISetUsersInstrumentBalance)
|
|||||
|
Defined in src/user/user.service.ts:412
|
|||||
|
Parameters :
Returns :
unknown
|
| Async subscribeDealerToNewFund | |||||||||
subscribeDealerToNewFund(dealer: UserDocument, subscribeUserDto: ISubscribeUser)
|
|||||||||
|
Defined in src/user/user.service.ts:435
|
|||||||||
|
Parameters :
Returns :
unknown
|
| Async subscribeInvestorToNewFund | |||||||||
subscribeInvestorToNewFund(user: UserDocument | UserWithOnChainFields, subscribedFund: string[])
|
|||||||||
|
Defined in src/user/user.service.ts:534
|
|||||||||
|
Parameters :
Returns :
unknown
|
| Async update | |||||
update(undefined: IUpdateUser)
|
|||||
|
Defined in src/user/user.service.ts:472
|
|||||
|
Parameters :
Returns :
Promise<UserDocument>
|
| Async userFilesUploader | ||||||
userFilesUploader(user: UserEntity)
|
||||||
|
Defined in src/user/user.service.ts:598
|
||||||
|
Parameters :
Returns :
unknown
|
| Async userSubscribeToNewFund | ||||||
userSubscribeToNewFund(subscribeUserDto: ISubscribeUser)
|
||||||
|
Defined in src/user/user.service.ts:359
|
||||||
|
Parameters :
Returns :
unknown
|
| Private Readonly fundAdminService |
Type : FundAdminService
|
Decorators :
@Inject(FundAdminService)
|
|
Defined in src/user/user.service.ts:105
|
| Private Readonly internalCustodialService |
Type : InternalCustodialService
|
Decorators :
@Inject(InternalCustodialService)
|
|
Defined in src/user/user.service.ts:101
|
| Private Readonly onChainDataFieldService |
Type : OnChainDataFieldService
|
Decorators :
@Inject(OnChainDataFieldService)
|
|
Defined in src/user/user.service.ts:103
|
| Private Readonly s3Service |
Type : S3Service
|
|
Defined in src/user/user.service.ts:106
|
| Private Readonly walletService |
Type : WalletService
|
Decorators :
@Inject(WalletService)
|
|
Defined in src/user/user.service.ts:99
|
import { UserDocument } from "src/user/schemas/user.schema";
import {
LeanDocument,
Model,
ObjectId,
Types,
isValidObjectId,
} from "mongoose";
import {
BadRequestException,
Inject,
Injectable,
ServiceUnavailableException,
UnauthorizedException,
} from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import {
decodeBytes32String,
encodeBytes32String,
formatEther,
hexlify,
keccak256,
toUtf8Bytes,
} from "ethers";
import type {
IAddedWallet,
IDealerIsAllowed,
IGetFundSubscriptionDetails,
IGetInvestorBalanceByInstrument,
ISetUsersInstrumentBalance,
ISubscribeUser,
IUpdateUser,
IUserOnChainDataField,
UserWithOnChainFields,
} from "./user.interface";
import type {
IBatch,
ICmsField,
IGetAllFundInstrumentsByFundIdAndInvestorIdQuery,
IGetGroupOfInvestorsRegistryFieldsQuery,
IGetInstrumentStAddressQuery,
IGetUserInstrumentBalanceQuery,
UserJwtPayload,
} from "src/common/interfaces";
import {
ContractName,
EndUserRoles,
QueryNames,
TransactionNames,
RegistryName,
} from "src/common/interfaces";
import { S3Service } from "src/common/provider/storage/s3";
import { User } from "./schemas/user.schema";
import { UserEntity } from "./entities/user.entity";
import { WalletService } from "src/wallet/wallet.service";
import { FilterUserDto } from "./dto/filter-user.dto";
import {
apolloClient,
getAllFundInstrumentsByFundIdAndInvestorIdQuery,
getGroupOfInvestorsRegistryFieldsQuery,
getUserInstrumentBalanceQuery,
transactionQuery,
transactionSubmitter,
} from "src/common/provider";
import {
getContractByName,
getRoleAndContractAddressByNameOrAddress,
} from "src/common/provider/web3/web3Provider";
import { InternalCustodialService } from "src/shared/custodial/internalCustodial.service";
import {
DEFAULT_LIBRE_DEALER,
INIT_SET_BATCH,
TypeToDefaultValue,
} from "src/common/constants";
import { ApolloQueryResult } from "@apollo/client";
import { getInstrumentStAddressQuery } from "src/common/provider/thegraph/queries/instrument.qgl";
import { BankDetailsDTO } from "src/shared/dto/bank-details.dto";
import { BankDetailsSchema } from "./../shared/schemas/BankDetails.schema";
import { OnChainDataFieldService } from "src/shared/onchainDataFields/onchainDataField.service";
import { DataFieldType } from "src/admin/EternalRegistry.interface";
import type { ISetRegistryDataFields } from "src/profile/profile.interface";
import type { OnChainFieldDTO } from "src/shared/dto/on-chain-field.dto";
import {
getUserWithUnSignedUrl,
mergeCmsFields,
orderGroupRegistryFields,
uploadCmsFileFields,
} from "src/common/utils";
import { FundAdminService } from "./fundAdmin.service";
@Injectable()
export class UserService {
@Inject(WalletService)
private readonly walletService: WalletService;
@Inject(InternalCustodialService)
private readonly internalCustodialService: InternalCustodialService;
@Inject(OnChainDataFieldService)
private readonly onChainDataFieldService: OnChainDataFieldService;
@Inject(FundAdminService)
private readonly fundAdminService: FundAdminService;
private readonly s3Service: S3Service;
constructor(
@InjectModel(User.name) private readonly userModel: Model<UserDocument>,
) {
this.s3Service = new S3Service();
}
async create(createUser: UserEntity): Promise<UserDocument> {
const userWithFiles = await this.userFilesUploader(createUser);
const { subscribedFunds, ...userRest } = userWithFiles;
const user = await this.userModel.create({
...userRest,
subscribedFunds:
userRest.endUserRole === "investor" &&
userRest.onboardByEmail !== DEFAULT_LIBRE_DEALER
? []
: subscribedFunds,
});
if (userRest.endUserRole === "investor") {
await this.fundAdminService.create(
subscribedFunds.map((fund) => {
return {
userId: user._id,
fundId: fund._id,
};
}),
);
}
return user;
}
async deleteByEmail(email: string): Promise<void> {
const user = await this.userModel.findOne({ email }).lean().exec();
if (user.endUserRole === EndUserRoles.investor) {
await this.fundAdminService.deleteAccessRequestForUser(user._id);
}
await this.userModel.deleteOne({ email }).exec();
}
async findByIds(ids: string[]) {
const users = await this.userModel
.find({ _id: { $in: ids } })
.populate("subscribedFunds")
.lean()
.exec();
return users.map((user) => {
return { ...user, ...user.bankDetails };
});
}
async findUserByProperty(
filterUserDto: FilterUserDto,
hasSubscribedFundsPopulated?: boolean,
): Promise<LeanDocument<UserDocument>> {
if (hasSubscribedFundsPopulated) {
return this.userModel
.findOne(filterUserDto)
.populate("subscribedFunds")
.lean()
.exec();
}
return this.userModel.findOne(filterUserDto).lean().exec();
}
async findOne(
id: string,
hasOnChainFields?: boolean,
): Promise<UserWithOnChainFields | User> {
const user = await this.userModel
.findById(id)
.populate({ path: "subscribedFunds", select: "title" })
.lean()
.exec();
if (!user) {
return null;
}
const userWitSignedUrl = await getUserWithUnSignedUrl(user);
return hasOnChainFields
? (await this.getUsersOnChainFields([userWitSignedUrl]))[0]
: userWitSignedUrl;
}
async findUsersByFilter(
filterUserDto: FilterUserDto,
includeOnChainField?: boolean,
) {
try {
const { limit, skip, orderDirection, ...restOfFilter } = filterUserDto;
const arraysFilter = Object.keys(restOfFilter).filter((userFilter) =>
Array.isArray(restOfFilter[userFilter]),
);
const arrayQuery = {};
arraysFilter.forEach((arrayFilter) => {
arrayQuery[arrayFilter] = {
$in: restOfFilter[arrayFilter].map((filterValue) => {
if (isValidObjectId(filterValue)) {
return Types.ObjectId.createFromHexString(filterValue);
}
return filterValue;
}),
};
});
const users: LeanDocument<UserDocument>[] = await this.userModel
.find(
{ ...restOfFilter, ...arrayQuery },
restOfFilter.selectors ? restOfFilter.selectors.join(" ") : "",
)
.limit(limit)
.skip(skip * limit)
.sort({ created: orderDirection })
.populate({ path: "subscribedFunds", select: "title fundType _id" })
.lean()
.exec();
if (
(restOfFilter.selectors &&
!restOfFilter.selectors.includes("onChainFields")) ||
!includeOnChainField
) {
return users;
}
return await this.getUsersOnChainFields(users);
} catch (error) {
console.error(error);
throw new ServiceUnavailableException(error);
}
}
async getUsersOnChainFields(
users: LeanDocument<UserDocument>[],
): Promise<UserWithOnChainFields[]> {
const usersIdInBytes = users.map((user) =>
encodeBytes32String(user._id.toString()),
);
const dataFieldsInfo: ApolloQueryResult<IGetGroupOfInvestorsRegistryFieldsQuery> =
await apolloClient.query({
query: getGroupOfInvestorsRegistryFieldsQuery,
variables: {
ids: usersIdInBytes,
contractAddress: getContractByName["JurisdictionRegistry"].address,
},
});
const hashes: string[] = [];
const getFieldByHashAndUserId: Record<
string,
Record<string, IUserOnChainDataField>
> = {};
const isJurisdictionHash: Record<string, boolean> = {};
dataFieldsInfo.data.userFields.forEach((field) => {
if (!getFieldByHashAndUserId[field.fieldId]) {
getFieldByHashAndUserId[field.fieldId] = {};
}
getFieldByHashAndUserId[field.fieldId][field.key] = {
userId: field.fieldId,
fieldName: "", // we didn't fetch it yet from the db
fieldType: field.type,
fieldValue: field.value,
};
hashes.push(field.key);
});
dataFieldsInfo.data.fieldsToLink.forEach((field) => {
isJurisdictionHash[field.key] = true;
hashes.push(field.key);
});
const onChainFields = await this.onChainDataFieldService.findByHashes(
hashes,
);
onChainFields.forEach((field) => {
Object.keys(getFieldByHashAndUserId).forEach((userId) => {
if (getFieldByHashAndUserId[userId][field.hash]) {
getFieldByHashAndUserId[userId][field.hash] = {
...getFieldByHashAndUserId[userId][field.hash],
fieldName: field.name,
};
}
if (getFieldByHashAndUserId[userId][field.linkedHash]) {
getFieldByHashAndUserId[userId][field.linkedHash] = {
...getFieldByHashAndUserId[userId][field.linkedHash],
fieldName: field.linkedName,
};
}
// if key doesn't exist mean it is from the Jurisdiction values and we didn't set the value onchain before yet
else {
getFieldByHashAndUserId[userId][field.linkedHash] = {
userId,
fieldName: field.linkedName,
fieldType: field.linkedType,
fieldValue: TypeToDefaultValue[field.linkedType],
};
}
});
});
return users.map((user) => {
const userIdInBytes = encodeBytes32String(user._id.toString());
const allFieldsKey = getFieldByHashAndUserId[userIdInBytes]
? Object.keys(getFieldByHashAndUserId[userIdInBytes])
: [];
const onChainFields: OnChainFieldDTO[] = allFieldsKey
.filter(
(key) => getFieldByHashAndUserId[userIdInBytes][key].fieldName.length,
)
.map((key) => {
const { fieldName, fieldType, fieldValue } =
getFieldByHashAndUserId[userIdInBytes][key];
return {
name: fieldName,
type: fieldType as DataFieldType,
value: fieldValue,
};
});
return {
...user,
onChainFields,
};
});
}
async findUsersByFilterByUsersBalance(
filterUserDto: FilterUserDto,
unconfirmedRedemptionAmountById: Record<string, string>,
) {
try {
const [users, getInstrumentStAddress]: [
LeanDocument<UserDocument>[] | UserWithOnChainFields[],
ApolloQueryResult<IGetInstrumentStAddressQuery>,
] = await Promise.all([
this.findUsersByFilter(filterUserDto, true),
apolloClient.query({
query: getInstrumentStAddressQuery,
variables: {
instrumentId: encodeBytes32String(filterUserDto.instrumentId),
},
}),
]);
return this.setUsersInstrumentBalance({
users: users as UserDocument[],
tokenAddress:
getInstrumentStAddress.data.instruments[0].securityToken.id,
unconfirmedRedemptionAmountById,
});
} catch (error) {
console.error(error);
throw new ServiceUnavailableException(error);
}
}
async userSubscribeToNewFund(subscribeUserDto: ISubscribeUser) {
try {
const updateAction = subscribeUserDto.isSubscribed
? { $addToSet: { subscribedFunds: subscribeUserDto.fundId } }
: { $pull: { subscribedFunds: subscribeUserDto.fundId } };
const user = await this.userModel
.findOne({
_id: subscribeUserDto.userId,
})
.exec();
if (!user) {
throw new BadRequestException(
`No user with id ${subscribeUserDto.userId}`,
);
}
if (
user.endUserRole === EndUserRoles.investor &&
subscribeUserDto.isSubscribed
) {
return await this.subscribeInvestorToNewFund(user, [
subscribeUserDto.fundId,
]);
}
if (
user.endUserRole === EndUserRoles.dealer &&
subscribeUserDto.isSubscribed
) {
if (!subscribeUserDto.instrumentId) {
throw new BadRequestException(
"For dealer you need to include the instrument id",
);
}
await this.subscribeDealerToNewFund(user, subscribeUserDto);
}
const updatedUser = await this.userModel
.findOneAndUpdate({ _id: subscribeUserDto.userId }, updateAction, {
new: true,
})
.populate("subscribedFunds")
.exec();
return updatedUser;
} catch (error) {
console.error(error);
if (error.status === 401) {
throw new BadRequestException(error);
}
throw new ServiceUnavailableException(error);
}
}
async setUsersInstrumentBalance({
tokenAddress,
users,
unconfirmedRedemptionAmountById,
}: ISetUsersInstrumentBalance) {
return Promise.all(
users.map(async (user) => {
const balance = await transactionQuery({
contractAddress: tokenAddress,
queryName: QueryNames.balanceOf,
contractName: ContractName.erc20,
args: [user.wallets[0].address],
});
return {
...user,
unconfirmedRedemption:
unconfirmedRedemptionAmountById[user._id.toString()],
balance: formatEther(balance),
};
}),
);
}
async subscribeDealerToNewFund(
dealer: UserDocument,
subscribeUserDto: ISubscribeUser,
) {
if (!subscribeUserDto.instrumentId) {
throw new BadRequestException(
"Dealer need an instrument Id to subscribe",
);
}
try {
const dealerId = encodeBytes32String(dealer.id);
const { contractAddress, role } =
await getRoleAndContractAddressByNameOrAddress({
contractName: ContractName.DealerRegistry,
transactionName: TransactionNames.allowDealer,
userAddress: process.env.ADMIN_WALLET_ADDRESS,
});
const tx = await transactionSubmitter({
signerKey: process.env.RELAYER_KEY,
contractAddress,
contractName: ContractName.DealerRegistry,
transactionName: TransactionNames.allowDealer,
args: [
role,
dealerId,
encodeBytes32String(subscribeUserDto.instrumentId),
subscribeUserDto.isSubscribed,
],
});
return tx;
} catch (error) {
console.error(error);
throw new ServiceUnavailableException(error);
}
}
async update({
id,
updateUserDto,
userJwtPayload,
}: IUpdateUser): Promise<UserDocument> {
const cmsFields: ICmsField[] = [];
if (updateUserDto.cmsFields) {
cmsFields.push(...(await uploadCmsFileFields(updateUserDto.cmsFields)));
}
const bankDetails: Pick<
InstanceType<typeof BankDetailsDTO>,
keyof BankDetailsDTO
> = Object.keys(updateUserDto)
.filter((key) => BankDetailsSchema.requiredPaths().includes(key))
.reduce((obj, key) => {
return {
...obj,
[`bankDetails.${key}`]: updateUserDto[key],
};
}, {} as Pick<InstanceType<typeof BankDetailsDTO>, keyof BankDetailsDTO>);
if (updateUserDto.onChainFields) {
await this.setRegistryData({
registryName: RegistryName.InvestorRegistry,
registries: orderGroupRegistryFields(
updateUserDto.onChainFields.map((registry) => {
return {
id: encodeBytes32String(id),
dataFieldType: registry.type,
dataFieldName: registry.name,
dataFieldValue: registry.value,
};
}),
),
userJwtPayload,
onchainFields: [],
});
}
let legalDocuments;
const user = await this.userModel.findById(id).exec();
if (updateUserDto.legalDocuments) {
legalDocuments = await this.s3Service.upload(
updateUserDto.legalDocuments,
`legalDocuments-${
updateUserDto.legalDocuments.originalname.split(".")[0]
}-${new Date().getTime()}-${user.endUserRole}.${
updateUserDto.legalDocuments.originalname.split(".")[1]
}`,
);
}
const updateFilter = {
...updateUserDto,
...bankDetails,
legalDocuments,
cmsFields: mergeCmsFields(user.cmsFields, cmsFields),
};
return this.userModel.findByIdAndUpdate(id, updateFilter, { new: true });
}
async subscribeInvestorToNewFund(
user: UserDocument | UserWithOnChainFields,
subscribedFund: string[],
) {
return await this.fundAdminService.create(
subscribedFund.map((fund) => {
return {
fundId: Types.ObjectId.createFromHexString(
fund,
) as unknown as ObjectId,
userId: user._id.toString(),
};
}),
);
}
async getFundSubscriptionDetails({
userId,
fundId,
}: IGetFundSubscriptionDetails) {
const investorSubscriptionInfo: ApolloQueryResult<IGetAllFundInstrumentsByFundIdAndInvestorIdQuery> =
await apolloClient.query({
query: getAllFundInstrumentsByFundIdAndInvestorIdQuery,
variables: {
userId: encodeBytes32String(userId),
fundId: encodeBytes32String(fundId),
},
});
if (investorSubscriptionInfo.errors) {
console.error(investorSubscriptionInfo.errors[0]);
throw new ServiceUnavailableException(investorSubscriptionInfo.errors);
}
const instrumentDetails: {
instrumentId: string;
instrumentName: string;
isInvestorSubscribed: boolean;
}[] = [];
const isSubscribedToInstrument: Record<string, boolean> = {};
investorSubscriptionInfo.data.whitelistInfo?.subscribedInstrument.forEach(
(instrument) => {
isSubscribedToInstrument[instrument.id] = true;
},
);
investorSubscriptionInfo.data.instruments.forEach((instrument) => {
instrumentDetails.push({
instrumentId: decodeBytes32String(instrument.id),
instrumentName: instrument.securityToken.name,
isInvestorSubscribed: isSubscribedToInstrument[instrument.id] || false,
});
});
return instrumentDetails;
}
async addNewWallet({ id, walletLabel }: IAddedWallet) {
const newWallet = await this.walletService.create({
walletLabel: walletLabel || "Wallet One",
});
this.userModel.findByIdAndUpdate(id, {
$push: { wallets: newWallet },
});
return newWallet;
}
async userFilesUploader(user: UserEntity) {
if (!user.legalDocuments) {
return user;
}
const legalDocuments = await this.s3Service.upload(
user.legalDocuments,
`legalDocuments-${
user.legalDocuments.originalname.split(".")[0]
}-${new Date().getTime()}-${user.endUserRole}.${
user.legalDocuments.originalname.split(".")[1]
}`,
);
const cmsFields = await uploadCmsFileFields(user.cmsFields);
const userWithUploadedFiles: User = {
...user,
legalDocuments,
endUserRole: user.endUserRole.toString(),
cmsFields,
};
return userWithUploadedFiles;
}
async isDealerAllowed({ investorId, dealerEmail, fundId }: IDealerIsAllowed) {
const [user, dealer] = await Promise.all([
this.userModel.findById(investorId),
this.findUserByProperty({ email: dealerEmail }),
]);
if (!user) {
return false;
}
let isSubscribedToTheFund = true;
if (fundId) {
isSubscribedToTheFund =
dealer.subscribedFunds.find((fund) => fund.toString() === fundId) !==
undefined;
}
return user.onboardByEmail === dealerEmail && isSubscribedToTheFund;
}
async checkIfUserIsAllowedToFund(userJwt: UserJwtPayload, fundId: string) {
if (userJwt.role === "admin") {
return true;
}
const user = await this.findUserByProperty({
email: userJwt.email,
});
if (
user.subscribedFunds.findIndex((fund) => {
return fund.toString() === fundId;
}) === -1
) {
throw new UnauthorizedException(
`${user.endUserRole} is not subscribed to this fund`,
);
}
}
async setRegistryData(setRegistryDataFields: ISetRegistryDataFields) {
try {
const batch = this.convertToBatch(setRegistryDataFields);
let signerKey = process.env.RELAYER_KEY;
let user: LeanDocument<UserDocument>;
if (setRegistryDataFields.userJwtPayload.role !== "admin") {
user = await this.findUserByProperty({
email: setRegistryDataFields.userJwtPayload.email,
});
signerKey = (
await this.internalCustodialService.findOne(user.wallets[0].address)
).privateKey;
}
const { contractAddress, role } =
await getRoleAndContractAddressByNameOrAddress({
contractName: ContractName.EternalRegistry,
transactionName: TransactionNames.setBatch,
contractAddress:
getContractByName[setRegistryDataFields.registryName].address,
userAddress:
setRegistryDataFields.userJwtPayload.role !== "admin"
? user.wallets[0].address
: process.env.ADMIN_WALLET_ADDRESS,
});
return await Promise.all([
transactionSubmitter({
signerKey,
transactionName: TransactionNames.setBatch,
contractName: ContractName.EternalRegistry,
contractAddress,
args: [
batch._id,
batch._keys,
batch._boolValues.map((boolValue) => boolValue === "true"),
batch._uintValues,
batch._addressValues,
batch._stringValues,
batch._bytesValues,
role,
],
}),
this.onChainDataFieldService.create(
setRegistryDataFields.onchainFields,
),
]);
} catch (error) {
console.log(error);
throw new ServiceUnavailableException(error);
}
}
convertToBatch(setRegistryDataFields: ISetRegistryDataFields): IBatch {
const batch = JSON.parse(JSON.stringify(INIT_SET_BATCH)); // to avoid shadow referencing the immutable object
setRegistryDataFields.registries.forEach(
async ({ id, dataFieldType, dataFieldName, dataFieldValue }) => {
batch._id.push(id);
batch._keys.push(keccak256(hexlify(toUtf8Bytes(dataFieldName))));
if (dataFieldType === DataFieldType.Bool) {
batch._boolValues.push(dataFieldValue);
}
if (dataFieldType === DataFieldType.Uint) {
batch._uintValues.push(dataFieldValue);
}
if (dataFieldType === DataFieldType.Address) {
batch._addressValues.push(dataFieldValue);
}
if (dataFieldType === DataFieldType.String) {
batch._stringValues.push(dataFieldValue);
}
if (dataFieldType === DataFieldType.Bytes) {
batch._bytesValues.push(dataFieldValue);
}
},
);
return batch;
}
async getCustodial(email: string, address?: string) {
if (address) {
return this.internalCustodialService.findOne(address);
}
const userByEmail = await this.findUserByProperty({ email });
return this.internalCustodialService.findOne(
userByEmail.wallets[0].address,
);
}
async getInvestorBalanceByInstrument({
instrumentId,
investor,
}: IGetInvestorBalanceByInstrument) {
const whitelistInfo: ApolloQueryResult<IGetUserInstrumentBalanceQuery> =
await apolloClient.query({
query: getUserInstrumentBalanceQuery,
variables: {
instrumentId: encodeBytes32String(instrumentId),
investorId: encodeBytes32String(investor._id.toString()),
},
});
if (whitelistInfo.error) {
new ServiceUnavailableException(whitelistInfo.error);
}
if (!whitelistInfo.data.instrument) {
new BadRequestException("Incorrect instrument ID");
}
let shares = 0;
await Promise.all(
investor.wallets.map(async (wallet) => {
const balance = await transactionQuery({
contractAddress: whitelistInfo.data.instrument.securityToken.id,
queryName: QueryNames.balanceOf,
contractName: ContractName.erc20,
args: [wallet.address],
});
shares += Number(formatEther(balance));
}),
);
return {
shares,
unConfirmedRedeemedAmount: formatEther(
whitelistInfo.data.whitelistInfo.unConfirmedRedeemedAmount,
),
nav: whitelistInfo.data.instrument?.navs.length
? whitelistInfo.data.instrument.navs[0].navPerShare
: 0,
name: whitelistInfo.data.instrument.securityToken.name,
symbol: whitelistInfo.data.instrument.securityToken.symbol,
};
}
}