mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-04-13 06:27:53 +03:00
Initial commit
This commit is contained in:
36
src/utils/calculateFeeInFeeAsset.ts
Normal file
36
src/utils/calculateFeeInFeeAsset.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { FILL_ORDERS_GAS_LIMIT } from '../constants';
|
||||
import calculateNetworkFeeInFeeAsset from './calculateNetworkFeeInFeeAsset';
|
||||
import calculateOrionFeeInFeeAsset from './calculateOrionFeeInFeeAsset';
|
||||
|
||||
const calculateFeeInFeeAsset = (
|
||||
amount: BigNumber.Value,
|
||||
feeAssetPriceInOrn: BigNumber.Value,
|
||||
baseAssetPriceInOrn: BigNumber.Value,
|
||||
baseCurrencyPriceInOrn: BigNumber.Value,
|
||||
gasPriceGwei: BigNumber.Value,
|
||||
feePercent: BigNumber.Value,
|
||||
) => {
|
||||
const orionFeeInFeeAsset = calculateOrionFeeInFeeAsset(
|
||||
amount,
|
||||
feeAssetPriceInOrn,
|
||||
baseAssetPriceInOrn,
|
||||
feePercent,
|
||||
);
|
||||
const networkFeeInFeeAsset = calculateNetworkFeeInFeeAsset(
|
||||
gasPriceGwei,
|
||||
FILL_ORDERS_GAS_LIMIT,
|
||||
baseCurrencyPriceInOrn,
|
||||
feeAssetPriceInOrn,
|
||||
);
|
||||
|
||||
return {
|
||||
orionFeeInFeeAsset,
|
||||
networkFeeInFeeAsset,
|
||||
totalFeeInFeeAsset: new BigNumber(orionFeeInFeeAsset)
|
||||
.plus(networkFeeInFeeAsset)
|
||||
.toString(),
|
||||
};
|
||||
};
|
||||
|
||||
export default calculateFeeInFeeAsset;
|
||||
13
src/utils/calculateNetworkFee.ts
Normal file
13
src/utils/calculateNetworkFee.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { ethers } from 'ethers';
|
||||
import { NATIVE_CURRENCY_PRECISION } from '../constants/precisions';
|
||||
|
||||
export default function calculateNetworkFee(
|
||||
gasPriceGwei: BigNumber.Value,
|
||||
gasLimit: BigNumber.Value,
|
||||
) {
|
||||
const networkFeeGwei = new BigNumber(gasPriceGwei).multipliedBy(gasLimit);
|
||||
|
||||
const bn = new BigNumber(ethers.utils.parseUnits(networkFeeGwei.toString(), 'gwei').toString());
|
||||
return bn.div(new BigNumber(10).pow(NATIVE_CURRENCY_PRECISION)).toString();
|
||||
}
|
||||
22
src/utils/calculateNetworkFeeInFeeAsset.ts
Normal file
22
src/utils/calculateNetworkFeeInFeeAsset.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import calculateNetworkFee from './calculateNetworkFee';
|
||||
|
||||
const calculateNetworkFeeInFeeAsset = (
|
||||
gasPriceGwei: BigNumber.Value,
|
||||
gasLimit: BigNumber.Value,
|
||||
baseCurrencyPriceInOrn: BigNumber.Value,
|
||||
feeAssetPriceInOrn: BigNumber.Value,
|
||||
) => {
|
||||
const networkFee = calculateNetworkFee(gasPriceGwei, gasLimit);
|
||||
|
||||
const networkFeeInOrn = new BigNumber(networkFee).multipliedBy(baseCurrencyPriceInOrn);
|
||||
const networkFeeInFeeAsset = networkFeeInOrn
|
||||
.multipliedBy(
|
||||
new BigNumber(1)
|
||||
.div(feeAssetPriceInOrn),
|
||||
);
|
||||
|
||||
return networkFeeInFeeAsset.toString();
|
||||
};
|
||||
|
||||
export default calculateNetworkFeeInFeeAsset;
|
||||
16
src/utils/calculateOrionFeeInFeeAsset.ts
Normal file
16
src/utils/calculateOrionFeeInFeeAsset.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export default function calculateOrionFeeInFeeAsset(
|
||||
amount: BigNumber.Value,
|
||||
feeAssetPriceInOrn: BigNumber.Value,
|
||||
baseAssetPriceInOrn: BigNumber.Value,
|
||||
feePercent: BigNumber.Value,
|
||||
) {
|
||||
const result = new BigNumber(amount)
|
||||
.multipliedBy(new BigNumber(feePercent).div(100))
|
||||
.multipliedBy(baseAssetPriceInOrn)
|
||||
.multipliedBy(new BigNumber(1).div(feeAssetPriceInOrn))
|
||||
.toString();
|
||||
|
||||
return result;
|
||||
}
|
||||
26
src/utils/checkIsToken.ts
Normal file
26
src/utils/checkIsToken.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { ethers } from 'ethers';
|
||||
import invariant from 'tiny-invariant';
|
||||
import { ERC20__factory } from '../artifacts/contracts';
|
||||
|
||||
const checkIsToken = async (address: string, provider?: ethers.providers.Provider) => {
|
||||
invariant(provider, 'No provider for token checking');
|
||||
const tokenContract = ERC20__factory.connect(address, provider);
|
||||
try {
|
||||
const results = await Promise.all(
|
||||
[
|
||||
tokenContract.name(),
|
||||
tokenContract.symbol(),
|
||||
tokenContract.decimals(),
|
||||
tokenContract.totalSupply(),
|
||||
tokenContract.balanceOf(ethers.constants.AddressZero),
|
||||
],
|
||||
);
|
||||
|
||||
return Boolean(results);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default checkIsToken;
|
||||
14
src/utils/denormalizeNumber.ts
Normal file
14
src/utils/denormalizeNumber.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
/**
|
||||
* Converts normalized blockchain ("machine-readable") number to denormalized ("human-readable") number.
|
||||
* @param input Any blockchain-normalized numeric value
|
||||
* @param decimals Blockchain asset precision
|
||||
* @returns BigNumber
|
||||
*/
|
||||
export default function denormalizeNumber(input: ethers.BigNumber, decimals: BigNumber.Value) {
|
||||
const decimalsBN = new BigNumber(decimals);
|
||||
if (!decimalsBN.isInteger()) throw new Error(`Decimals '${decimals.toString()}' is not an integer`);
|
||||
return new BigNumber(input.toString()).div(new BigNumber(10).pow(decimalsBN));
|
||||
}
|
||||
12
src/utils/generateSecret.ts
Normal file
12
src/utils/generateSecret.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import rand from 'csprng';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
const generateSecret = () => {
|
||||
const RANDOM_RADIX = 16;
|
||||
const RANDOM_BITS = 256;
|
||||
const random = rand(RANDOM_BITS, RANDOM_RADIX);
|
||||
const secret = ethers.utils.keccak256(`0x${random}`);
|
||||
return secret;
|
||||
};
|
||||
|
||||
export default generateSecret;
|
||||
19
src/utils/getAvailableFundsSources.ts
Normal file
19
src/utils/getAvailableFundsSources.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { Source } from '../types';
|
||||
|
||||
export default function getAvailableFundsSources(
|
||||
expenseType: 'amount' | 'network_fee' | 'orion_fee',
|
||||
assetAddress: string,
|
||||
route: 'aggregator' | 'orion_pool',
|
||||
): Source[] {
|
||||
switch (route) {
|
||||
case 'aggregator':
|
||||
if (assetAddress === ethers.constants.AddressZero) return ['exchange']; // We can't take native crypto from wallet
|
||||
return ['exchange', 'wallet']; // We can take any token amount from exchange + wallet. Order is important!
|
||||
case 'orion_pool':
|
||||
if (expenseType === 'network_fee') return ['wallet']; // Network fee is always taken from wallet
|
||||
return ['exchange', 'wallet']; // We can take any token amount from exchange + wallet (specify 'value' for 'orion_pool'). Order is important!
|
||||
default:
|
||||
throw new Error('Unknown route item');
|
||||
}
|
||||
}
|
||||
57
src/utils/getBalance.ts
Normal file
57
src/utils/getBalance.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { ethers } from 'ethers';
|
||||
import { contracts, utils } from '..';
|
||||
import { INTERNAL_ORION_PRECISION, NATIVE_CURRENCY_PRECISION } from '../constants';
|
||||
import { OrionAggregator } from '../services/OrionAggregator';
|
||||
|
||||
export default async function getBalance(
|
||||
orionAggregator: OrionAggregator,
|
||||
asset: string,
|
||||
assetAddress: string,
|
||||
walletAddress: string,
|
||||
exchangeContract: contracts.Exchange,
|
||||
provider: ethers.providers.Provider,
|
||||
) {
|
||||
const assetIsNativeCryptocurrency = assetAddress === ethers.constants.AddressZero;
|
||||
|
||||
let assetWalletBalance: ethers.BigNumber | undefined;
|
||||
let assetAllowance: ethers.BigNumber | undefined;
|
||||
|
||||
let denormalizedAssetInWalletBalance: BigNumber | undefined;
|
||||
let denormalizedAssetInAllowance: BigNumber | undefined;
|
||||
|
||||
if (!assetIsNativeCryptocurrency) {
|
||||
const assetContract = contracts.ERC20__factory.connect(assetAddress, provider);
|
||||
const assetDecimals = await assetContract.decimals();
|
||||
assetWalletBalance = await assetContract.balanceOf(walletAddress);
|
||||
assetAllowance = await assetContract.allowance(walletAddress, exchangeContract.address);
|
||||
|
||||
denormalizedAssetInWalletBalance = utils.denormalizeNumber(assetWalletBalance, assetDecimals);
|
||||
denormalizedAssetInAllowance = utils.denormalizeNumber(assetAllowance, assetDecimals);
|
||||
} else {
|
||||
assetWalletBalance = await provider.getBalance(walletAddress);
|
||||
denormalizedAssetInWalletBalance = utils.denormalizeNumber(assetWalletBalance, NATIVE_CURRENCY_PRECISION);
|
||||
denormalizedAssetInAllowance = denormalizedAssetInWalletBalance; // For native crypto no allowance is needed
|
||||
}
|
||||
const assetContractBalance = await exchangeContract.getBalance(assetAddress, walletAddress);
|
||||
const denormalizedAssetInContractBalance = utils.denormalizeNumber(assetContractBalance, INTERNAL_ORION_PRECISION);
|
||||
// const denormalizedAssetWalletBalanceAvailable = BigNumber.min(denormalizedAssetInAllowance, denormalizedAssetInWalletBalance);
|
||||
const denormalizedAssetLockedBalance = await orionAggregator.getLockedBalance(walletAddress, asset);
|
||||
|
||||
// const denormalizedAssetAvailableBalance = denormalizedAssetInContractBalance
|
||||
// .plus(denormalizedAssetWalletBalanceAvailable)
|
||||
// .minus(denormalizedAssetLockedBalance[asset] ?? 0);
|
||||
// const denormalizedAssetImaginaryBalance = denormalizedAssetInContractBalance
|
||||
// .plus(denormalizedAssetInWalletBalance)
|
||||
// .minus(denormalizedAssetLockedBalance[asset] ?? 0);
|
||||
|
||||
return {
|
||||
exchange: denormalizedAssetInContractBalance.minus(denormalizedAssetLockedBalance[asset] ?? 0),
|
||||
// imaginary: denormalizedAssetImaginaryBalance,
|
||||
wallet: denormalizedAssetInWalletBalance,
|
||||
allowance: denormalizedAssetInAllowance,
|
||||
// approvedWalletBalance: denormalizedAssetWalletBalanceAvailable,
|
||||
// available: denormalizedAssetAvailableBalance,
|
||||
// locked: denormalizedAssetLockedBalance[asset],
|
||||
};
|
||||
}
|
||||
41
src/utils/getBalances.ts
Normal file
41
src/utils/getBalances.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { ethers } from 'ethers';
|
||||
import { contracts } from '..';
|
||||
import { OrionAggregator } from '../services/OrionAggregator';
|
||||
import getBalance from './getBalance';
|
||||
|
||||
export default async (
|
||||
balancesRequired: Partial<Record<string, string>>,
|
||||
orionAggregator: OrionAggregator,
|
||||
walletAddress: string,
|
||||
exchangeContract: contracts.Exchange,
|
||||
provider: ethers.providers.Provider,
|
||||
) => {
|
||||
const balances = await Promise.all(
|
||||
Object.entries(balancesRequired)
|
||||
.map(async ([asset, assetAddress]) => {
|
||||
if (!assetAddress) throw new Error(`Asset address of ${asset} not found`);
|
||||
const balance = await getBalance(
|
||||
orionAggregator,
|
||||
asset,
|
||||
assetAddress,
|
||||
walletAddress,
|
||||
exchangeContract,
|
||||
provider,
|
||||
);
|
||||
return {
|
||||
asset,
|
||||
amount: balance,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return balances.reduce<Partial<Record<string, {
|
||||
exchange: BigNumber,
|
||||
wallet: BigNumber,
|
||||
allowance: BigNumber,
|
||||
}>>>((prev, curr) => ({
|
||||
...prev,
|
||||
[curr.asset]: curr.amount,
|
||||
}), {});
|
||||
};
|
||||
26
src/utils/getSwapPair.ts
Normal file
26
src/utils/getSwapPair.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// exact spend
|
||||
|
||||
// n X -> USDT - пара X-USDT (продажа амаунта на паре X-USDT)
|
||||
// n USDT -> X - пара X-USDT (покупка на стоимость на паре X-USDT)
|
||||
// n X -> Y - пара X-Y (продажа амаунта на паре X-Y)
|
||||
|
||||
// exact eceive
|
||||
|
||||
// X -> n USDT - пара X-USDT (продажа на стоимость на паре X-USDT)
|
||||
// USDT -> n X - пара X-USDT (покупка амаунта на паре X-USDT)
|
||||
// X -> n Y - пара Y-X (покупка амаунта на паре Y-X)
|
||||
|
||||
export default function getSwapPair(
|
||||
assetIn: string | null | undefined,
|
||||
assetOut: string | null | undefined,
|
||||
type: 'exactSpend' | 'exactReceive',
|
||||
) {
|
||||
if (!assetIn || !assetOut) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (assetOut === 'USDT') return `${assetIn}-USDT`;
|
||||
if (assetIn === 'USDT') return `${assetOut}-USDT`;
|
||||
if (type === 'exactSpend') return `${assetIn}-${assetOut}`;
|
||||
return `${assetOut}-${assetIn}`;
|
||||
}
|
||||
14
src/utils/getSwapSide.ts
Normal file
14
src/utils/getSwapSide.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export default function getSwapSide(
|
||||
assetIn: string | null | undefined,
|
||||
assetOut: string | null | undefined,
|
||||
type: 'exactSpend' | 'exactReceive',
|
||||
) {
|
||||
if (!assetIn || !assetOut) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (assetOut === 'USDT') return 'SELL';
|
||||
if (assetIn === 'USDT') return 'BUY';
|
||||
if (type === 'exactSpend') return 'SELL';
|
||||
return 'BUY';
|
||||
}
|
||||
35
src/utils/hashOrder.ts
Normal file
35
src/utils/hashOrder.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { Order } from '../types';
|
||||
|
||||
const hashOrder = (order: Order) => ethers.utils.solidityKeccak256(
|
||||
[
|
||||
'uint8',
|
||||
'address',
|
||||
'address',
|
||||
'address',
|
||||
'address',
|
||||
'address',
|
||||
'uint64',
|
||||
'uint64',
|
||||
'uint64',
|
||||
'uint64',
|
||||
'uint64',
|
||||
'uint8',
|
||||
],
|
||||
[
|
||||
'0x03',
|
||||
order.senderAddress,
|
||||
order.matcherAddress,
|
||||
order.baseAsset,
|
||||
order.quoteAsset,
|
||||
order.matcherFeeAsset,
|
||||
order.amount,
|
||||
order.price,
|
||||
order.matcherFee,
|
||||
order.nonce,
|
||||
order.expiration,
|
||||
order.buySide ? '0x01' : '0x00',
|
||||
],
|
||||
);
|
||||
|
||||
export default hashOrder;
|
||||
17
src/utils/httpError.ts
Normal file
17
src/utils/httpError.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export default class HttpError extends Error {
|
||||
public code: number;
|
||||
|
||||
public errorMessage: string |null;
|
||||
|
||||
public statusText: string;
|
||||
|
||||
public type: string;
|
||||
|
||||
constructor(code:number, message:string|null, type: string, statusText:string) {
|
||||
super(message || '');
|
||||
this.errorMessage = message;
|
||||
this.type = type;
|
||||
this.statusText = statusText;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
17
src/utils/index.ts
Normal file
17
src/utils/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export { default as calculateFeeInFeeAsset } from './calculateFeeInFeeAsset';
|
||||
export { default as calculateNetworkFee } from './calculateNetworkFee';
|
||||
export { default as calculateNetworkFeeInFeeAsset } from './calculateNetworkFeeInFeeAsset';
|
||||
export { default as calculateOrionFeeInFeeAsset } from './calculateOrionFeeInFeeAsset';
|
||||
export { default as hashOrder } from './hashOrder';
|
||||
export { default as checkIsToken } from './checkIsToken';
|
||||
export { default as signOrderPersonal } from './signOrderPersonal';
|
||||
export { default as generateSecret } from './generateSecret';
|
||||
export { default as isValidChainId } from './isValidChainId';
|
||||
export { default as denormalizeNumber } from './denormalizeNumber';
|
||||
export { default as normalizeNumber } from './normalizeNumber';
|
||||
export { default as getSwapPair } from './getSwapPair';
|
||||
export { default as getSwapSide } from './getSwapSide';
|
||||
|
||||
export { default as HttpError } from './httpError';
|
||||
|
||||
export * from './typeHelpers';
|
||||
9
src/utils/isValidChainId.ts
Normal file
9
src/utils/isValidChainId.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
import { SupportedChainId } from '../types';
|
||||
|
||||
const isValidChainId = (chainId: string): chainId is SupportedChainId => {
|
||||
const { success } = z.nativeEnum(SupportedChainId).safeParse(chainId);
|
||||
return success;
|
||||
};
|
||||
|
||||
export default isValidChainId;
|
||||
25
src/utils/normalizeNumber.ts
Normal file
25
src/utils/normalizeNumber.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
/**
|
||||
* Converts denormalized ("human-readable") number to normalized ("machine-readable") number.
|
||||
* @param input Any numeric value
|
||||
* @param decimals Blockchain asset precision
|
||||
* @param roundingMode Rounding mode
|
||||
* @returns ethers.BigNumber
|
||||
*/
|
||||
export default function normalizeNumber(
|
||||
input: BigNumber.Value,
|
||||
decimals: BigNumber.Value,
|
||||
roundingMode: BigNumber.RoundingMode,
|
||||
) {
|
||||
const decimalsBN = new BigNumber(decimals);
|
||||
if (!decimalsBN.isInteger()) throw new Error(`Decimals '${decimals.toString()}' is not an integer`);
|
||||
const inputBN = new BigNumber(input);
|
||||
return ethers.BigNumber.from(
|
||||
inputBN
|
||||
.multipliedBy(new BigNumber(10).pow(decimals))
|
||||
.integerValue(roundingMode)
|
||||
.toString(),
|
||||
);
|
||||
}
|
||||
33
src/utils/signOrderPersonal.ts
Normal file
33
src/utils/signOrderPersonal.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { Order } from '../types';
|
||||
|
||||
const { arrayify, joinSignature, splitSignature } = ethers.utils;
|
||||
|
||||
const signOrderPersonal = async (order: Order, signer: ethers.Signer) => {
|
||||
const message = ethers.utils.solidityKeccak256(
|
||||
[
|
||||
'string', 'address', 'address', 'address', 'address',
|
||||
'address', 'uint64', 'uint64', 'uint64', 'uint64', 'uint64', 'uint8',
|
||||
],
|
||||
[
|
||||
'order',
|
||||
order.senderAddress,
|
||||
order.matcherAddress,
|
||||
order.baseAsset,
|
||||
order.quoteAsset,
|
||||
order.matcherFeeAsset,
|
||||
order.amount,
|
||||
order.price,
|
||||
order.matcherFee,
|
||||
order.nonce,
|
||||
order.expiration,
|
||||
order.buySide,
|
||||
],
|
||||
);
|
||||
const signature = await signer.signMessage(arrayify(message));
|
||||
|
||||
// NOTE: metamask broke sig.v value and we fix it in next line
|
||||
return joinSignature(splitSignature(signature));
|
||||
};
|
||||
|
||||
export default signOrderPersonal;
|
||||
71
src/utils/typeHelpers.ts
Normal file
71
src/utils/typeHelpers.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
type WithReason = {
|
||||
reason: string;
|
||||
}
|
||||
|
||||
type WithCodeError = Error & {
|
||||
code: number;
|
||||
}
|
||||
|
||||
type WithMessage = {
|
||||
message: string;
|
||||
}
|
||||
|
||||
type WithDataError = Error & {
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
|
||||
type WithError ={
|
||||
error: Record<string | number | symbol, unknown>;
|
||||
}
|
||||
|
||||
export const makePartial = <Key extends string | number | symbol, Value>(value: Record<Key, Value>): Partial<Record<Key, Value>> => value;
|
||||
|
||||
export function isUnknownObject(x: unknown): x is {
|
||||
[key in PropertyKey]: unknown
|
||||
} {
|
||||
return x !== null && typeof x === 'object';
|
||||
}
|
||||
|
||||
export function isKeyOfObject<T>(
|
||||
key: string | number | symbol,
|
||||
obj: T,
|
||||
): key is keyof T {
|
||||
return key in obj;
|
||||
}
|
||||
|
||||
export function hasProp<T extends Record<string, unknown>, K extends PropertyKey>(
|
||||
obj: T,
|
||||
key: K,
|
||||
): obj is T & Record<K, unknown> {
|
||||
return key in obj;
|
||||
}
|
||||
|
||||
export function isWithCode(candidate: unknown): candidate is WithCodeError {
|
||||
if (!isUnknownObject(candidate)) return false;
|
||||
const hasCodeProperty = hasProp(candidate, 'code') && typeof candidate.code === 'number';
|
||||
return hasCodeProperty;
|
||||
}
|
||||
|
||||
export function isWithReason(candidate: unknown): candidate is WithReason {
|
||||
if (!isUnknownObject(candidate)) return false;
|
||||
const hasReasonProperty = hasProp(candidate, 'reason') && typeof candidate.reason === 'string';
|
||||
return hasReasonProperty;
|
||||
}
|
||||
|
||||
export function isWithMessage(candidate: unknown): candidate is WithMessage {
|
||||
if (!isUnknownObject(candidate)) return false;
|
||||
const hasMessageProperty = hasProp(candidate, 'message') && typeof candidate.message === 'string';
|
||||
return hasMessageProperty;
|
||||
}
|
||||
|
||||
export function isWithError(candidate: unknown): candidate is WithError {
|
||||
if (!isUnknownObject(candidate)) return false;
|
||||
const hasErrorProperty = hasProp(candidate, 'error') && isUnknownObject(candidate.error);
|
||||
return hasErrorProperty;
|
||||
}
|
||||
|
||||
export function isWithData(candidate: unknown): candidate is WithDataError {
|
||||
if (!isUnknownObject(candidate)) return false;
|
||||
const hasDataProperty = hasProp(candidate, 'data') && typeof candidate.data === 'object';
|
||||
return hasDataProperty;
|
||||
}
|
||||
Reference in New Issue
Block a user