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
33 changes: 5 additions & 28 deletions src/app/offers/ValidOffer.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
import { LedgerService } from "@services/ledger"
import Offer from "./Offer"
import { PaymentSendStatus } from "@domain/bitcoin/lightning"
import { LedgerServiceError } from "@domain/ledger"
import { ValidationError } from "@domain/shared"
import {
accountLevel,
isActiveAccount,
isUsd,
hasSufficientBalance,
transferMin,
validate,
walletBelongsToAccount,
transferMax,
isBeforeExpiry,
hasErpParty,
} from "./Validations"
import { CashoutValidator } from "./Validator"
import { RepositoryError } from "@domain/errors"
import { AccountsRepository, WalletsRepository } from "@services/mongoose"
import Ibex from "@services/ibex/client"
Expand Down Expand Up @@ -47,21 +34,11 @@ class ValidOffer extends Offer {
if (account instanceof RepositoryError) return new ValidationError(account)

const inputs: ValidationInputs = { ...details, wallet, account }
const validationErrs = await validate(inputs, [
isUsd,
transferMin,
transferMax,
isActiveAccount,
accountLevel,
walletBelongsToAccount,
hasSufficientBalance,
isBeforeExpiry,
hasErpParty,
// TODO daily/weekly/monthly volume limits
])
if (validationErrs.length > 0) return new ValidationError(validationErrs)
const validation = await CashoutValidator(inputs)

return new ValidOffer(inputs)
if (Array.isArray(validation)) return new ValidationError(validation)

return new ValidOffer(validation)
}

async execute(): Promise<InitiatedCashout | Error> {
Expand Down
50 changes: 22 additions & 28 deletions src/app/offers/Validations.ts → src/app/offers/Validator.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
import { getBalanceForWallet } from "@app/wallets";
import { Cashout } from "@config";
import { AccountValidator } from "@domain/accounts";
import { USDAmount, ValidationError } from "@domain/shared";
import { ValidationFn, ValidationInputs } from "./types";
import { AccountValidator, hasErpParty, isActiveAccount, walletBelongsToAccount } from "@domain/accounts";
import { USDAmount, ValidationError, ValidationFn, validator } from "@domain/shared";
import { ValidationInputs } from "./types";

const config = Cashout.validations

export const isBeforeExpiry = async (o: ValidationInputs): Promise<true | ValidationError> => {
const isBeforeExpiry = async (o: ValidationInputs): Promise<true | ValidationError> => {
const now = new Date()
if (now > o.ibexTrx.invoice.expiresAt) return new ValidationError("Offer has expired")
else return true
}

export const transferMin = async (o: ValidationInputs): Promise<true | ValidationError> => {
const cashoutMin = async (o: ValidationInputs): Promise<true | ValidationError> => {
const min = USDAmount.cents(config.minimum.amount)
if (min instanceof Error) return new ValidationError(min)
if (o.ibexTrx.usd.isLesserThan(min))
return new ValidationError(`Minimum cashout is $${min.asDollars()}`)
else return true
}

export const transferMax = async (o: ValidationInputs): Promise<true | ValidationError> => {
const cashoutMax: ValidationFn<ValidationInputs> = async (o: ValidationInputs): Promise<true | ValidationError> => {
const max = USDAmount.cents(config.maximum.amount)
if (max instanceof Error) return new ValidationError(max)
if (o.ibexTrx.usd.isGreaterThan(max) )
return new ValidationError(`Maximum cashout is $${max.asDollars()}`)
else return true
}
}

export const isUsd = async (o: ValidationInputs) => {
const isUsd = async (o: ValidationInputs) => {
// if (o.ibexTrx.currency !== "USD")
// return new ValidationError("Cash out only supports USD")
if (o.wallet.currency !== "USD")
return new ValidationError("Cash out only supports withdrawals from USD wallets")
return true
}

export const hasSufficientBalance = async (o: ValidationInputs): Promise<true | ValidationError> => {
const hasSufficientBalance = async (o: ValidationInputs): Promise<true | ValidationError> => {
const balance = await getBalanceForWallet({ walletId: o.wallet.id })
if (balance instanceof Error)
return new ValidationError(balance)
Expand All @@ -45,27 +45,21 @@ export const hasSufficientBalance = async (o: ValidationInputs): Promise<true |
else return true
}

export const isActiveAccount = async (o: ValidationInputs) => {
return AccountValidator(o.account).isActive()
}

export const accountLevel = async (o: ValidationInputs) => {
const accountLevel = async (o: ValidationInputs) => {
return AccountValidator(o.account).isLevel(config.accountLevel)
}

export const walletBelongsToAccount = async (o: ValidationInputs) => {
return AccountValidator(o.account).validateWalletForAccount(o.wallet)
}

// TODO: Look this field up against ERP system to ensure it is valid
export const hasErpParty = async (o: ValidationInputs): Promise<true | ValidationError> => {
if (!o.account.erpParty) {
return new ValidationError("Account is missing erpParty field.")
}
return true
}

export const validate = async (inputs: ValidationInputs, validators: ValidationFn[]): Promise<ValidationError[]> => {
const results = await Promise.all(validators.map(v => v(inputs)))
return results.filter((r): r is ValidationError => (r !== true))
};
export const CashoutValidator = validator([
isUsd,
cashoutMin,
cashoutMax,
isActiveAccount,
accountLevel,
walletBelongsToAccount,
hasSufficientBalance,
isBeforeExpiry,
hasErpParty,
// TODO daily/weekly/monthly volume limits
])
1 change: 0 additions & 1 deletion src/app/offers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export type ValidationInputs = CashoutDetails & {
wallet: Wallet,
account: Account
}
export type ValidationFn = (inputs: ValidationInputs) => Promise<true | ValidationError>;

// Rtgs is Jamaican bank transfer
export type RtgsTransfer = {
Expand Down
22 changes: 0 additions & 22 deletions src/app/wallets/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,28 +107,6 @@ type IntraLedgerPaymentSendWalletIdArgs = PaymentSendArgs & {
amount: number
}

type PayAllOnChainByWalletIdArgs = {
senderWalletId: WalletId
senderAccount: Account
address: string
speed: PayoutSpeed
memo: string | null
}

type PayOnChainByWalletIdWithoutCurrencyArgs = {
senderWalletId: WalletId
senderAccount: Account
amount: number
address: string
speed: PayoutSpeed
memo: string | null
}

type PayOnChainByWalletIdArgs = PayOnChainByWalletIdWithoutCurrencyArgs & {
amountCurrency: WalletCurrency | undefined
sendAll: boolean
}

type PayOnChainByWalletIdResult = {
status: PaymentSendStatus
payoutId: PayoutId | undefined
Expand Down
Loading