Untitled

mail@pastecode.io avatar
unknown
plain_text
2 months ago
2.8 kB
1
Indexable
Never
import * as Dinero from "dinero.js";
import * as B from "fp-ts/boolean";
import { pipe } from "fp-ts/lib/function";
import * as TE from "fp-ts/TaskEither";

import { fromCode } from "@lambdas/shared/money";
import { ValidateQuoteResponsePayload } from "@lambdas/shared/services/fx/validate-quote-by-fx-id/ValidateQuoteResponseParser";
import * as DepositAccount from "@lambdas/shared/services/mambu/depositAccount";
import {
  NoAccountError,
  noAccountError,
} from "@lambdas/shared/services/mambu/error";

import { ExchangedAmountNotMatchError } from "./ExchangedAmountNotMatchError";
import { RequestPayload } from "./parser";

const CHOC_EXCHANGE_FEES = 0.005;

export type ExchangeInfo = {
  fromWithdrawalAccount: DepositAccount.DepositAccount;
  toDepositAccount: DepositAccount.DepositAccount;
  depositAmount: Dinero.Dinero<number>;
};

const getAccountByName = (
  depositAccounts: DepositAccount.DepositAccount[],
  accountName: DepositAccount.DepositAccountName,
  userId: string
) =>
  pipe(
    depositAccounts,
    DepositAccount.findByAccountName(accountName),
    TE.fromOption(() => noAccountError(userId, accountName))
  );

export const getExchangeInfo = (
  depositAccounts: DepositAccount.DepositAccount[],
  validateQuote: ValidateQuoteResponsePayload,
  userId: string,
  payload: RequestPayload
): TE.TaskEither<NoAccountError | ExchangedAmountNotMatchError, ExchangeInfo> =>
  pipe(
    getAccountByName(depositAccounts, payload.fromAccountName, userId),
    TE.bindTo("fromWithdrawalAccount"),
    TE.bindW("toDepositAccount", () =>
      getAccountByName(depositAccounts, payload.toAccountName, userId)
    ),
    TE.bindW("depositAmount", () =>
      TE.of(
        calculateDepositAmount(
          payload.amount,
          validateQuote.exchangeRate,
          payload
        )
      )
    ),
    TE.chainFirstW(({ depositAmount }) =>
      validateExchangedAmount(payload.exchangedAmount, depositAmount)
    ),
    TE.map((depositAccount) => depositAccount)
  );

const calculateDepositAmount = (
  amount: Dinero.Dinero<number>,
  rate: number,
  payload: RequestPayload
) => {
  const fees = Dinero.toUnit(amount) * CHOC_EXCHANGE_FEES;
  const exchangedAmount = (Dinero.toUnit(amount) - fees) * rate;
  return fromCode(payload.toCurrency).amount(exchangedAmount);
};

const validateExchangedAmount = (
  exchangedAmount: Dinero.Dinero<number>,
  depositAmount: Dinero.Dinero<number>
) =>
  pipe(
    Dinero.equal(exchangedAmount, depositAmount),
    B.fold(
      () => TE.of(undefined),
      // TODO: uncomment - Waiting for validateQuoteFx
      // TE.left(
      //   exchangedAmountNotMatchError(
      //     Dinero.toUnit(exchangedAmount),
      //     Dinero.toUnit(depositAmount)
      //   )
      // ),
      () => TE.of(undefined)
    )
  );
Leave a Comment