Skip to content

Commit

Permalink
Create a base of the swap preview;
Browse files Browse the repository at this point in the history
Update the new order request;
Add the mutable type
  • Loading branch information
skubarenko committed Aug 19, 2022
1 parent 872dadb commit ae84315
Show file tree
Hide file tree
Showing 22 changed files with 279 additions and 78 deletions.
45 changes: 39 additions & 6 deletions src/atomex/atomex.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import BigNumber from 'bignumber.js';

import type { AuthorizationManager } from '../authorization/index';
import type { WalletsManager } from '../blockchain/index';
import type { AtomexService, Currency } from '../common/index';
import type { ExchangeManager } from '../exchange/exchangeManager';
import { NewOrderRequest, ExchangeManager, symbolsHelper } from '../exchange/index';
import type { Swap, SwapManager } from '../swaps/index';
import type { AtomexContext } from './atomexContext';
import {
SwapOperationCompleteStage, AtomexOptions,
NewSwapRequest, AtomexBlockchainNetworkOptions
NewSwapRequest, SwapOperationCompleteStage, AtomexOptions,
AtomexBlockchainNetworkOptions, SwapPreview, SwapPreviewParameters
} from './models/index';

export class Atomex implements AtomexService {
Expand Down Expand Up @@ -70,21 +72,52 @@ export class Atomex implements AtomexService {
return this.atomexContext.providers.currenciesProvider.getCurrency(currencyId);
}

async getSwapPreview(_swapPreviewParameters: SwapPreviewParameters): Promise<SwapPreview> {
throw new Error('Not implemented');
}

async swap(newSwapRequest: NewSwapRequest, completeStage?: SwapOperationCompleteStage): Promise<Swap | readonly Swap[]>;
async swap(swapId: Swap['id'], completeStage?: SwapOperationCompleteStage): Promise<Swap | readonly Swap[]>;
async swap(newSwapRequestOrSwapId: NewSwapRequest | Swap['id'], _completeStage = SwapOperationCompleteStage.All): Promise<Swap | readonly Swap[]> {
if (typeof newSwapRequestOrSwapId === 'number')
throw new Error('Swap tracking is not implemented yet');

const orderId = await this.exchangeManager.addOrder(newSwapRequestOrSwapId.accountAddress, newSwapRequestOrSwapId);
const order = await this.exchangeManager.getOrder(newSwapRequestOrSwapId.accountAddress, orderId);
const swapPreview = newSwapRequestOrSwapId.swapPreview;
const quoteCurrencyId = symbolsHelper.getQuoteBaseCurrenciesBySymbol(swapPreview.symbol)[0];
const directionName: 'from' | 'to' = quoteCurrencyId === swapPreview.from.currencyId ? 'from' : 'to';

const rewardForRedeem = swapPreview.fees.success.find(fee => fee.name == 'rewardForRedeem')?.estimated;
const newOrderRequest: NewOrderRequest = {
orderBody: {
type: swapPreview.type,
symbol: swapPreview.symbol,
side: swapPreview.side,
amount: swapPreview[directionName].actual.amount,
price: swapPreview[directionName].actual.price
},
requisites: {
secretHash: null,
receivingAddress: swapPreview.to.address,
refundAddress: newSwapRequestOrSwapId.refundAddress || null,
rewardForRedeem: rewardForRedeem || new BigNumber(0),
// TODO: from config
lockTime: 18000,
baseCurrencyContract: '',
quoteCurrencyContract: ''
},
proofsOfFunds: [
// TODO
]
};
const orderId = await this.exchangeManager.addOrder(swapPreview.from.address, newOrderRequest);
const order = await this.exchangeManager.getOrder(swapPreview.from.address, orderId);
if (!order)
throw new Error(`The ${orderId} order not found`);

if (order.status !== 'Filled')
throw new Error(`The ${orderId} order is not filled`);

const swaps = await Promise.all(order.swapIds.map(swapId => this.swapManager.getSwap(swapId, newSwapRequestOrSwapId.accountAddress)));
const swaps = await Promise.all(order.swapIds.map(swapId => this.swapManager.getSwap(swapId, swapPreview.from.address)));
if (!swaps.length)
throw new Error('Swaps not found');
if (swaps.some(swap => !swap))
Expand Down
2 changes: 2 additions & 0 deletions src/atomex/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ export type {
AtomexManagers, AtomexServices
} from './atomexOptions';
export type { NewSwapRequest } from './newSwapRequest';
export type { SwapPreviewParameters } from './swapPreviewParameters';
export type { SwapPreview } from './swapPreview';

export { SwapOperationCompleteStage } from './swapOperationCompleteStage';
10 changes: 6 additions & 4 deletions src/atomex/models/newSwapRequest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { NewOrderRequest } from '../../exchange/index';
import type { SwapPreview } from './swapPreview';

export type NewSwapRequest = NewOrderRequest & {
readonly accountAddress: string;
};
export interface NewSwapRequest {
swapPreview: SwapPreview;
refundAddress?: string;
isInitiator?: boolean
}
48 changes: 48 additions & 0 deletions src/atomex/models/swapPreview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type BigNumber from 'bignumber.js';

import type { Currency, Side } from '../../common/index';
import type { OrderType } from '../../exchange/index';

export interface SwapPreview {
readonly type: OrderType;
readonly from: SwapPreviewData;
readonly to: SwapPreviewData;
readonly symbol: string;
readonly side: Side;
readonly fees: {
success: readonly SwapPreviewFee[];
refund: readonly SwapPreviewFee[];
}
readonly errors: readonly SwapPreviewError[];
readonly warnings: readonly SwapPreviewWarning[];
}

interface SwapPreviewData {
readonly address: string;
readonly currencyId: Currency['id'];
readonly actual: SwapPreviewCurrencyData;
readonly available: SwapPreviewCurrencyData;
readonly max?: SwapPreviewCurrencyData;
}

interface SwapPreviewCurrencyData {
readonly amount: BigNumber;
readonly price: BigNumber;
}

interface SwapPreviewError<TErrorData = unknown> {
readonly id: string
readonly data: TErrorData;
}

interface SwapPreviewWarning<TWarningData = unknown> {
readonly id: string
readonly data: TWarningData;
}

interface SwapPreviewFee {
readonly name: string;
readonly currencyId: Currency['id'];
readonly estimated: BigNumber;
readonly max: BigNumber;
}
28 changes: 28 additions & 0 deletions src/atomex/models/swapPreviewParameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type BigNumber from 'bignumber.js';

import type { Side } from '../../common/index';
import type { CurrencyDirection, OrderType } from '../../exchange/index';

interface SwapPreviewParametersBase {
type: OrderType;
amount: BigNumber;
useWatchTower?: boolean;
}

export type SwapPreviewParameters = SwapPreviewParametersBase & (
| {
from: CurrencyDirection['from'];
to: CurrencyDirection['to'];
isFromAmount?: boolean;
symbol?: never;
side?: never;
isQuoteCurrencyAmount?: never;
}
| {
from?: never;
to?: never;
isFromAmount?: never;
symbol: string;
side: Side;
isQuoteCurrencyAmount?: boolean;
});
23 changes: 23 additions & 0 deletions src/clients/dtos.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ProofOfFundsAlgorithm } from '../exchange/index';

export type Side = 'Buy' | 'Sell';
export type OrderType = 'Return' | 'FillOrKill' | 'ImmediateOrCancel' | 'SolidFillOrKill';
export type OrderStatus = 'Pending' | 'Placed' | 'PartiallyFilled' | 'Filled' | 'Canceled' | 'Rejected';
Expand Down Expand Up @@ -44,6 +46,17 @@ export interface WebSocketOrderBookEntryDto extends OrderBookEntryDto {
updateId: number;
}

export interface NewOrderRequestDto {
clientOrderId: string;
symbol: string;
price: number;
qty: number;
side: Side;
type: OrderType;
proofsOfFunds?: ProofOfFundsDto[];
requisites?: SwapRequisitesDto;
}

export interface OrderDto {
id: number;
clientOrderId: string;
Expand Down Expand Up @@ -96,6 +109,16 @@ export interface SwapRequisitesDto {
quoteCurrencyContract: string;
}

export interface ProofOfFundsDto {
address: string;
currency: string;
timeStamp: number;
message: string;
publicKey: string;
signature: string;
algorithm: ProofOfFundsAlgorithm;
}

export interface TransactionDto {
currency: string;
txId: string;
Expand Down
7 changes: 1 addition & 6 deletions src/clients/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@ import BigNumber from 'bignumber.js';

import type { Transaction } from '../blockchain/models/index';
import type { CurrenciesProvider } from '../common/index';
import { ExchangeSymbol, ExchangeSymbolsProvider, NewOrderRequest, Order, OrderBook, OrderBookEntry, OrderBookProvider, OrderPreview, Quote, symbolsHelper } from '../exchange/index';
import { ExchangeSymbol, ExchangeSymbolsProvider, Order, OrderBook, OrderBookEntry, OrderBookProvider, Quote, symbolsHelper } from '../exchange/index';
import type { Swap, SwapParticipantTrade } from '../swaps/index';
import type {
OrderBookDto, OrderBookEntryDto, OrderDto, QuoteDto, SwapDto, SymbolDto,
TradeDto, TransactionDto, WebSocketOrderBookEntryDto, WebSocketOrderDataDto
} from './dtos';

export const isOrderPreview = (orderBody: NewOrderRequest['orderBody']): orderBody is OrderPreview => {
return typeof orderBody.symbol === 'string' && typeof orderBody.side === 'string'
&& !!(orderBody as OrderPreview).from && !!(orderBody as OrderPreview).to;
};

export const mapQuoteDtosToQuotes = (quoteDtos: QuoteDto[]): Quote[] => {
const quotes: Quote[] = quoteDtos.map(quoteDto => mapQuoteDtoToQuote(quoteDto));

Expand Down
4 changes: 2 additions & 2 deletions src/clients/mixedAtomexClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Transaction } from '../blockchain/index';
import type { AtomexNetwork } from '../common/index';
import type {
Order, OrderBook, Quote, ExchangeSymbol, NewOrderRequest,
Order, OrderBook, Quote, ExchangeSymbol, FilledNewOrderRequest,
OrdersSelector, CancelOrderRequest, CancelAllOrdersRequest,
SwapsSelector, CurrencyDirection
} from '../exchange/index';
Expand Down Expand Up @@ -81,7 +81,7 @@ export class MixedApiAtomexClient implements AtomexClient {
return (this.restAtomexClient.getOrderBook as (symbolOrDirection: string | CurrencyDirection) => Promise<OrderBook | undefined>)(symbolOrDirection);
}

addOrder(accountAddress: string, newOrderRequest: NewOrderRequest): Promise<number> {
addOrder(accountAddress: string, newOrderRequest: FilledNewOrderRequest): Promise<number> {
return this.restAtomexClient.addOrder(accountAddress, newOrderRequest);
}

Expand Down
30 changes: 12 additions & 18 deletions src/clients/rest/restAtomexClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import type { AtomexNetwork, CancelAllSide, CurrenciesProvider, Side } from '../
import { EventEmitter } from '../../core';
import { HttpClient } from '../../core/index';
import {
Order, OrderBook, Quote, ExchangeSymbol, NewOrderRequest,
OrdersSelector, CancelOrderRequest,
CancelAllOrdersRequest, SwapsSelector, CurrencyDirection, symbolsHelper, ExchangeSymbolsProvider
exchangeGuards,
Order, OrderBook, Quote, ExchangeSymbol, OrdersSelector, CancelOrderRequest,
CancelAllOrdersRequest, SwapsSelector, CurrencyDirection, symbolsHelper, ExchangeSymbolsProvider, FilledNewOrderRequest
} from '../../exchange/index';
import type { Swap } from '../../swaps/index';
import type { AtomexClient } from '../atomexClient';
import type { CreatedOrderDto, OrderBookDto, OrderDto, QuoteDto, SwapDto, SymbolDto } from '../dtos';
import type { CreatedOrderDto, NewOrderRequestDto, OrderBookDto, OrderDto, QuoteDto, SwapDto, SymbolDto } from '../dtos';
import {
isOrderPreview,
mapOrderBookDtoToOrderBook, mapOrderDtosToOrders, mapOrderDtoToOrder,
mapQuoteDtosToQuotes, mapSwapDtosToSwaps, mapSwapDtoToSwap, mapSymbolDtosToSymbols
} from '../helpers';
Expand Down Expand Up @@ -146,26 +145,21 @@ export class RestAtomexClient implements AtomexClient {
return orderBookDto ? mapOrderBookDtoToOrderBook(orderBookDto) : undefined;
}

async addOrder(accountAddress: string, newOrderRequest: NewOrderRequest): Promise<number> {
async addOrder(accountAddress: string, newOrderRequest: FilledNewOrderRequest): Promise<number> {
const urlPath = '/v1/Orders';
const authToken = this.getRequiredAuthToken(accountAddress);
let symbol: string | undefined = undefined;
let side: Side | undefined = undefined;
let amountBigNumber: BigNumber;
let priceBigNumber: BigNumber;

if (isOrderPreview(newOrderRequest.orderBody)) {
const exchangeSymbols = this.exchangeSymbolsProvider.getSymbolsMap();
const exchangeSymbolAndSide = symbolsHelper.findExchangeSymbolAndSide(
exchangeSymbols,
newOrderRequest.orderBody.from.currencyId,
newOrderRequest.orderBody.to.currencyId
);
const exchangeSymbol = exchangeSymbolAndSide[0];
symbol = exchangeSymbol.name;
side = exchangeSymbolAndSide[1];
if (exchangeGuards.isOrderPreview(newOrderRequest.orderBody)) {
symbol = newOrderRequest.orderBody.symbol;
side = newOrderRequest.orderBody.side;

const quoteCurrencyId = symbolsHelper.getQuoteBaseCurrenciesBySymbol(symbol)[0];
const directionName: 'from' | 'to' = quoteCurrencyId === newOrderRequest.orderBody.from.currencyId ? 'from' : 'to';

const directionName: 'from' | 'to' = exchangeSymbol.quoteCurrency === newOrderRequest.orderBody.from.currencyId ? 'from' : 'to';
amountBigNumber = newOrderRequest.orderBody[directionName].amount;
priceBigNumber = newOrderRequest.orderBody[directionName].price;
}
Expand All @@ -176,7 +170,7 @@ export class RestAtomexClient implements AtomexClient {
}

// TODO: move to the mapper
const payload = {
const payload: NewOrderRequestDto = {
clientOrderId: newOrderRequest.clientOrderId,
symbol,
side,
Expand Down
4 changes: 2 additions & 2 deletions src/clients/webSocket/webSocketAtomexClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Transaction } from '../../blockchain/index';
import type { AtomexNetwork, CurrenciesProvider } from '../../common/index';
import { DeferredEventEmitter, EventEmitter, ToDeferredEventEmitter, type ToEventEmitter } from '../../core';
import type {
Order, OrderBook, Quote, ExchangeSymbol, NewOrderRequest,
Order, OrderBook, Quote, ExchangeSymbol, FilledNewOrderRequest,
OrdersSelector, CancelOrderRequest,
CancelAllOrdersRequest, SwapsSelector, CurrencyDirection, ExchangeSymbolsProvider, ManagedOrderBookProvider
} from '../../exchange/index';
Expand Down Expand Up @@ -111,7 +111,7 @@ export class WebSocketAtomexClient implements AtomexClient {
throw new Error('Method not implemented.');
}

addOrder(_accountAddress: string, _newOrderRequest: NewOrderRequest): Promise<number> {
addOrder(_accountAddress: string, _newOrderRequest: FilledNewOrderRequest): Promise<number> {
throw new Error('Method not implemented.');
}

Expand Down
3 changes: 2 additions & 1 deletion src/core/httpClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
type QueryParams = { [key: string]: string | number | boolean | null | undefined };
type Payload = { [key: string]: unknown };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Payload = { [key: string]: any };

export interface RequestOptions {
urlPath: string;
Expand Down
1 change: 1 addition & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export { HttpClient, type RequestOptions } from './httpClient';
export type { Result, SuccessResult, ErrorResult } from './result';
export type { PublicEventEmitter, ToEventEmitter, ToEventEmitters } from './eventEmitter';
export type { ToDeferredEventEmitter, ToDeferredEventEmitters } from './deferredEventEmitter';
export type { Mutable } from './mutable';
export type { DeepReadonly } from './deepReadonly';
export type { DeepRequired } from './deepRequired';
export type { DeepPartial } from './deepPartial';
Expand Down
15 changes: 15 additions & 0 deletions src/core/mutable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

export type Mutable<T> = T extends ReadonlyMap<infer K, infer V>
? Map<K, V>
: T extends ReadonlySet<infer V>
? Set<V>
: T extends (...args: any) => any
? T
: T extends new (...args: any) => any
? T
: unknown extends T
? T
: {
-readonly [K in keyof T]: T[K]
};
14 changes: 11 additions & 3 deletions src/exchange/exchangeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { symbolsHelper } from './helpers/index';
import type {
CancelAllOrdersRequest, CancelOrderRequest, CurrencyDirection, ExchangeSymbol,
OrderPreviewParameters as OrderPreviewParameters,
NewOrderRequest, Order, OrderBook, OrderPreview, OrdersSelector, Quote, OrderType, PreparedPreviewParameters
NewOrderRequest, Order, OrderBook, OrderPreview, OrdersSelector, Quote, OrderType, PreparedPreviewParameters, FilledNewOrderRequest
} from './models/index';
import type { ManagedOrderBookProvider } from './orderBookProvider';

Expand Down Expand Up @@ -135,8 +135,16 @@ export class ExchangeManager implements AtomexService {
}

addOrder(accountAddress: string, newOrderRequest: NewOrderRequest): Promise<number> {
const clientOrderId = newOrderRequest.clientOrderId || nanoid(17);
return this.exchangeService.addOrder(accountAddress, { ...newOrderRequest, clientOrderId });
const filledNewOrderRequest: FilledNewOrderRequest = {
clientOrderId: newOrderRequest.clientOrderId || nanoid(17),
orderBody: newOrderRequest.orderBody,
requisites: newOrderRequest.requisites,
proofsOfFunds: newOrderRequest.proofsOfFunds ? newOrderRequest.proofsOfFunds : [
// TODO
]
};

return this.exchangeService.addOrder(accountAddress, filledNewOrderRequest);
}

cancelOrder(accountAddress: string, cancelOrderRequest: CancelOrderRequest): Promise<boolean> {
Expand Down
Loading

0 comments on commit ae84315

Please sign in to comment.