* Added new pairs type: futures

* Added new pairs type: futures

* added market param to pairsConfig request

* OP-3268 [CFD] Deposit amount

Added CFDContracts endpoint
package.json version update

* OP-3268 [CFD] Deposit amount

CFDContractsSchema export

* OP-3268 [CFD] Deposit amount

CFDContractsSchema export
package.json update

* cfdContractsSchema rename
Add getCFDHistory method
package.json update

* Parameter of getCFDHistory method was updated

* cfdHistorySchema was updated

* WS subscribtion for CFD balances, CFD Order signing functionality

* cfdHistorySchema was updated
package.json version 0.16.0-rc.6 was updated

* package.json version 0.16.0-rc.7 was updated

* Exchange and CFD history schemas was transformed
package.json version 0.16.0-rc.8 was updated

* Added HistoryTransactionStatus
package.json version 0.16.0-rc.8 was updated

* cfdContractsSchema was updated
package.json version 0.16.0-rc.10 was updated

* Merge conflicts fix
package.json version was updated to 0.16.0-rc.12

* updated schemas, updated version

* Updated CFD balance schema, updated unsubscribe with passing extra details

* CFD balances schema update

* Added CFD prices request

* typing small fix

* fixed transformation of CFD init / update responses

* temporary added debug

* temporary added debug. changed ver

* debug, ver up

* Add currentPrice to cfdBalanceSchema

* Added maxAvailableLong maxAvailableShort to balances

* added leverage and position status

* Update isWithCode - allow string code

* version up

Co-authored-by: Mikhail Gladchenko <m@gladchenko.pw>
Co-authored-by: Demid <demidn@gmail.com>
Co-authored-by: Dmitry Leleko <lelekodmitry@gmail.com>
This commit is contained in:
Aleksandr Kraiz
2023-01-27 15:45:10 +04:00
committed by GitHub
parent a7cc752de9
commit 1e3cf57c87
21 changed files with 551 additions and 21 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@orionprotocol/sdk",
"version": "0.15.31",
"name": "@orionprotocol/sdk",
"version": "0.16.0",
"description": "Orion Protocol SDK",
"main": "./lib/esm/index.js",
"module": "./lib/esm/index.js",

View File

@@ -0,0 +1,15 @@
const CFD_ORDER_TYPES = {
CFDOrder: [
{ name: 'senderAddress', type: 'address' },
{ name: 'matcherAddress', type: 'address' },
{ name: 'instrumentAddress', type: 'address' },
{ name: 'amount', type: 'uint64' },
{ name: 'price', type: 'uint64' },
{ name: 'matcherFee', type: 'uint64' },
{ name: 'nonce', type: 'uint64' },
{ name: 'expiration', type: 'uint64' },
{ name: 'buySide', type: 'uint8' },
],
};
export default CFD_ORDER_TYPES;

View File

@@ -0,0 +1,9 @@
const positionStatuses = [
'SHORT',
'LONG',
'CLOSED',
'LIQUIDATED',
'NOT_OPEN',
] as const;
export default positionStatuses;

31
src/crypt/hashCFDOrder.ts Normal file
View File

@@ -0,0 +1,31 @@
import { ethers } from 'ethers';
import { CFDOrder } from '../types';
const hashCFDOrder = (order: CFDOrder) => ethers.utils.solidityKeccak256(
[
'uint8',
'address',
'address',
'address',
'uint64',
'uint64',
'uint64',
'uint64',
'uint64',
'uint8',
],
[
'0x03',
order.senderAddress,
order.matcherAddress,
order.instrumentAddress,
order.amount,
order.price,
order.matcherFee,
order.nonce,
order.expiration,
order.buySide ? '0x01' : '0x00',
],
);
export default hashCFDOrder;

View File

@@ -1,4 +1,5 @@
export { default as signCancelOrder } from './signCancelOrder';
export { default as signCancelOrderPersonal } from './signCancelOrderPersonal';
export { default as signOrder } from './signOrder';
export { default as signCFDOrder } from './signCFDOrder';
export { default as signOrderPersonal } from './signOrderPersonal';

83
src/crypt/signCFDOrder.ts Normal file
View File

@@ -0,0 +1,83 @@
/* eslint-disable no-underscore-dangle */
import { TypedDataSigner } from '@ethersproject/abstract-signer';
import BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import { joinSignature, splitSignature } from 'ethers/lib/utils';
import { INTERNAL_ORION_PRECISION } from '../constants';
import { CFDOrder, SignedCFDOrder, SupportedChainId } from '../types';
import normalizeNumber from '../utils/normalizeNumber';
import getDomainData from './getDomainData';
import signCFDOrderPersonal from "./signCFDOrderPersonal";
import hashCFDOrder from "./hashCFDOrder";
import CFD_ORDER_TYPES from "../constants/cfdOrderTypes";
const DEFAULT_EXPIRATION = 29 * 24 * 60 * 60 * 1000; // 29 days
type SignerWithTypedDataSign = ethers.Signer & TypedDataSigner;
export const signCFDOrder = async (
instrumentAddress: string,
side: 'BUY' | 'SELL',
price: BigNumber.Value,
amount: BigNumber.Value,
matcherFee: BigNumber.Value,
senderAddress: string,
matcherAddress: string,
usePersonalSign: boolean,
signer: ethers.Signer,
chainId: SupportedChainId,
) => {
const nonce = Date.now();
const expiration = nonce + DEFAULT_EXPIRATION;
const order: CFDOrder = {
senderAddress,
matcherAddress,
instrumentAddress,
amount: normalizeNumber(
amount,
INTERNAL_ORION_PRECISION,
BigNumber.ROUND_FLOOR,
).toNumber(),
price: normalizeNumber(
price,
INTERNAL_ORION_PRECISION,
BigNumber.ROUND_FLOOR,
).toNumber(),
matcherFee: normalizeNumber(
matcherFee,
INTERNAL_ORION_PRECISION,
BigNumber.ROUND_CEIL, // ROUND_CEIL because we don't want get "not enough fee" error
).toNumber(),
nonce,
expiration,
buySide: side === 'BUY' ? 1 : 0,
isPersonalSign: usePersonalSign,
};
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const typedDataSigner = signer as SignerWithTypedDataSign;
const signature = usePersonalSign
? await signCFDOrderPersonal(order, signer)
: await typedDataSigner._signTypedData(
getDomainData(chainId),
CFD_ORDER_TYPES,
order,
);
// https://github.com/poap-xyz/poap-fun/pull/62#issue-928290265
// "Signature's v was always send as 27 or 28, but from Ledger was 0 or 1"
const fixedSignature = joinSignature(splitSignature(signature));
if (!fixedSignature) throw new Error("Can't sign order");
const signedOrder: SignedCFDOrder = {
...order,
id: hashCFDOrder(order),
signature: fixedSignature,
};
return signedOrder;
};
export default signCFDOrder;

View File

@@ -0,0 +1,30 @@
import { ethers } from 'ethers';
import { CFDOrder } from '../types';
const { arrayify, joinSignature, splitSignature } = ethers.utils;
const signCFDOrderPersonal = async (order: CFDOrder, signer: ethers.Signer) => {
const message = ethers.utils.solidityKeccak256(
[
'string', 'address', 'address', 'address', 'uint64', 'uint64', 'uint64', 'uint64', 'uint64', 'uint8',
],
[
'order',
order.senderAddress,
order.matcherAddress,
order.instrumentAddress,
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 signCFDOrderPersonal;

View File

@@ -9,12 +9,13 @@ import errorSchema from './schemas/errorSchema';
import placeAtomicSwapSchema from './schemas/placeAtomicSwapSchema';
import { OrionAggregatorWS } from './ws';
import { atomicSwapHistorySchema } from './schemas/atomicSwapHistorySchema';
import { Exchange, SignedCancelOrderRequest, SignedOrder } from '../../types';
import {Exchange, SignedCancelOrderRequest, SignedCFDOrder, SignedOrder} from '../../types';
import { pairConfigSchema } from './schemas';
import {
aggregatedOrderbookSchema, exchangeOrderbookSchema, poolReservesSchema,
} from './schemas/aggregatedOrderbookSchema';
import networkCodes from '../../constants/networkCodes';
import toUpperCase from '../../utils/toUpperCase';
class OrionAggregator {
private readonly apiUrl: string;
@@ -33,6 +34,7 @@ class OrionAggregator {
this.getTradeProfits = this.getTradeProfits.bind(this);
this.placeAtomicSwap = this.placeAtomicSwap.bind(this);
this.placeOrder = this.placeOrder.bind(this);
this.placeCFDOrder = this.placeCFDOrder.bind(this);
this.cancelOrder = this.cancelOrder.bind(this);
this.checkWhitelisted = this.checkWhitelisted.bind(this);
this.getLockedBalance = this.getLockedBalance.bind(this);
@@ -41,10 +43,15 @@ class OrionAggregator {
this.getPoolReserves = this.getPoolReserves.bind(this);
}
getPairsList = () => fetchWithValidation(
`${this.apiUrl}/api/v1/pairs/list`,
z.array(z.string()),
);
getPairsList = (market: 'spot' | 'futures') => {
const url = new URL(`${this.apiUrl}/api/v1/pairs/list`);
url.searchParams.append('market', toUpperCase(market));
return fetchWithValidation(
url.toString(),
z.array(z.string()),
);
};
getAggregatedOrderbook = (pair: string, depth = 20) => {
const url = new URL(`${this.apiUrl}/api/v1/orderbook`);
@@ -78,6 +85,18 @@ class OrionAggregator {
);
};
getPairConfigs = (market: 'spot' | 'futures') => {
const url = new URL(`${this.apiUrl}/api/v1/pairs/exchangeInfo`);
url.searchParams.append('market', toUpperCase(market));
return fetchWithValidation(
url.toString(),
exchangeInfoSchema,
undefined,
errorSchema,
);
}
getPoolReserves = (
pair: string,
exchange: Exchange,
@@ -91,13 +110,6 @@ class OrionAggregator {
);
};
getPairConfigs = () => fetchWithValidation(
`${this.apiUrl}/api/v1/pairs/exchangeInfo`,
exchangeInfoSchema,
undefined,
errorSchema,
);
getPairConfig = (assetPair: string) => fetchWithValidation(
`${this.apiUrl}/api/v1/pairs/exchangeInfo/${assetPair}`,
pairConfigSchema,
@@ -161,6 +173,35 @@ class OrionAggregator {
errorSchema,
);
placeCFDOrder = (
signedOrder: SignedCFDOrder
) => {
const headers = {
'Content-Type': 'application/json',
Accept: 'application/json',
};
return fetchWithValidation(
`${this.apiUrl}/api/v1/order/futures`,
z.object({
orderId: z.string(),
placementRequests: z.array(
z.object({
amount: z.number(),
brokerAddress: z.string(),
exchange: z.string(),
}),
).optional(),
}),
{
headers,
method: 'POST',
body: JSON.stringify(signedOrder),
},
errorSchema,
);
};
getSwapInfo = (
type: 'exactSpend' | 'exactReceive',
assetIn: string,

View File

@@ -7,6 +7,7 @@ const MessageType = {
ASSET_PAIRS_CONFIG_UPDATE: 'apcu',
ASSET_PAIR_CONFIG_UPDATE: 'apiu',
ADDRESS_UPDATE: 'au',
CFD_ADDRESS_UPDATE: 'auf',
BROKER_TRADABLE_ATOMIC_SWAP_ASSETS_BALANCE_UPDATE: 'btasabu',
UNSUBSCRIPTION_DONE: 'ud',
} as const;

View File

@@ -3,6 +3,7 @@ const SubscriptionType = {
ASSET_PAIR_CONFIG_UPDATES_SUBSCRIBE: 'apius',
AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE: 'aobus',
ADDRESS_UPDATES_SUBSCRIBE: 'aus',
CFD_ADDRESS_UPDATES_SUBSCRIBE: 'ausf',
BROKER_TRADABLE_ATOMIC_SWAP_ASSETS_BALANCE_UPDATES_SUBSCRIBE: 'btasabus',
SWAP_SUBSCRIBE: 'ss',
} as const;

View File

@@ -11,11 +11,12 @@ import {
import UnsubscriptionType from './UnsubscriptionType';
import {
SwapInfoByAmountIn, SwapInfoByAmountOut, SwapInfoBase,
AssetPairUpdate, OrderbookItem, Balance, Exchange,
AssetPairUpdate, OrderbookItem, Balance, Exchange, CFDBalance,
} from '../../../types';
import unsubscriptionDoneSchema from './schemas/unsubscriptionDoneSchema';
import assetPairConfigSchema from './schemas/assetPairConfigSchema';
import { fullOrderSchema, orderUpdateSchema } from './schemas/addressUpdateSchema';
import cfdAddressUpdateSchema from './schemas/cfdAddressUpdateSchema';
// import errorSchema from './schemas/errorSchema';
const UNSUBSCRIBE = 'u';
@@ -86,13 +87,31 @@ type AddressUpdateInitial = {
orders?: z.infer<typeof fullOrderSchema>[] // The field is not defined if the user has no orders
}
type CfdAddressUpdateUpdate = {
kind: 'update',
balances?: CFDBalance[],
order?: z.infer<typeof orderUpdateSchema> | z.infer<typeof fullOrderSchema>
}
type CfdAddressUpdateInitial = {
kind: 'initial',
balances: CFDBalance[],
orders?: z.infer<typeof fullOrderSchema>[] // The field is not defined if the user has no orders
}
type AddressUpdateSubscription = {
payload: string,
callback: (data: AddressUpdateUpdate | AddressUpdateInitial) => void,
}
type CfdAddressUpdateSubscription = {
payload: string,
callback: (data: CfdAddressUpdateUpdate | CfdAddressUpdateInitial) => void,
}
type Subscription = {
[SubscriptionType.ADDRESS_UPDATES_SUBSCRIBE]: AddressUpdateSubscription,
[SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE]: CfdAddressUpdateSubscription,
[SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE]: AggregatedOrderbookSubscription,
[SubscriptionType.ASSET_PAIRS_CONFIG_UPDATES_SUBSCRIBE]: PairsConfigSubscription,
[SubscriptionType.ASSET_PAIR_CONFIG_UPDATES_SUBSCRIBE]: PairConfigSubscription,
@@ -197,10 +216,11 @@ class OrionAggregatorWS {
return id;
}
unsubscribe(subscription: keyof typeof UnsubscriptionType | string) {
unsubscribe(subscription: keyof typeof UnsubscriptionType | string, details?: string) {
this.send({
T: UNSUBSCRIBE,
S: subscription,
d: details,
});
if (subscription.includes('0x')) { // is wallet address (ADDRESS_UPDATE)
@@ -212,6 +232,15 @@ class OrionAggregatorWS {
delete this.subscriptions[SubscriptionType.ADDRESS_UPDATES_SUBSCRIBE]?.[key];
}
}
const aufSubscriptions = this.subscriptions[SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE];
if (aufSubscriptions) {
const targetAufSub = Object.entries(aufSubscriptions).find(([, value]) => value?.payload === subscription);
if (targetAufSub) {
const [key] = targetAufSub;
delete this.subscriptions[SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE]?.[key];
}
}
} else if (uuidValidate(subscription)) {
// is swap info subscription (contains hyphen)
delete this.subscriptions[SubscriptionType.SWAP_SUBSCRIBE]?.[subscription];
@@ -277,6 +306,7 @@ class OrionAggregatorWS {
initMessageSchema,
pingPongMessageSchema,
addressUpdateSchema,
cfdAddressUpdateSchema,
assetPairsConfigSchema,
assetPairConfigSchema,
brokerMessageSchema,
@@ -425,6 +455,47 @@ class OrionAggregatorWS {
});
}
break;
case MessageType.CFD_ADDRESS_UPDATE: {
switch (json.k) { // message kind
case 'i': { // initial
const fullOrders = json.o
? json.o.reduce<z.infer<typeof fullOrderSchema>[]>((prev, o) => {
prev.push(o);
return prev;
}, [])
: undefined;
this.subscriptions[
SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE
]?.[json.id]?.callback({
kind: 'initial',
orders: fullOrders,
balances: json.b ?? [],
});
}
break;
case 'u': { // update
let orderUpdate: z.infer<typeof orderUpdateSchema> | z.infer<typeof fullOrderSchema> | undefined;
if (json.o) {
const firstOrder = json.o[0];
orderUpdate = firstOrder;
}
this.subscriptions[
SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE
]?.[json.id]?.callback({
kind: 'update',
order: orderUpdate,
balances: json.b,
});
}
break;
default:
break;
}
}
break;
case MessageType.ADDRESS_UPDATE: {
const balances = json.b
? Object.entries(json.b)

View File

@@ -0,0 +1,33 @@
import { z } from "zod";
import { fullOrderSchema, orderUpdateSchema } from "./addressUpdateSchema";
import baseMessageSchema from "./baseMessageSchema";
import MessageType from "../MessageType";
import cfdBalancesSchema from "./cfdBalancesSchema";
const baseCfdAddressUpdate = baseMessageSchema.extend({
id: z.string(),
T: z.literal(MessageType.CFD_ADDRESS_UPDATE),
S: z.string(), // subscription
uc: z.array(z.enum(['b', 'o'])), // update content
});
const updateMessageSchema = baseCfdAddressUpdate.extend({
k: z.literal('u'), // kind of message: "u" - updates
uc: z.array(z.enum(['b', 'o'])), // update content: "o" - orders updates, "b" - balance updates
b: cfdBalancesSchema.optional(),
o: z.tuple([fullOrderSchema.or(orderUpdateSchema)]).optional(),
});
const initialMessageSchema = baseCfdAddressUpdate.extend({
k: z.literal('i'), // kind of message: "i" - initial
b: cfdBalancesSchema,
o: z.array(fullOrderSchema)
.optional(), // When no orders — no field
});
const cfdAddressUpdateSchema = z.union([
initialMessageSchema,
updateMessageSchema,
]);
export default cfdAddressUpdateSchema

View File

@@ -0,0 +1,46 @@
import { z } from 'zod';
import positionStatuses from "../../../../constants/positionStatuses";
const cfdBalanceSchema = z
.object({
i: z.string(),
b: z.string(),
pnl: z.string(),
fr: z.string(),
e: z.string(),
p: z.string(),
cp: z.string(),
pp: z.string(),
r: z.string(),
m: z.string(),
mu: z.string(),
fmu: z.string(),
awb: z.string(),
mli: z.string(),
msi: z.string(),
l: z.string(),
s: z.enum(positionStatuses),
})
.transform((obj) => ({
instrument: obj.i,
balance: obj.b,
profitLoss: obj.pnl,
fundingRate: obj.fr,
equity: obj.e,
position: obj.p,
currentPrice: obj.cp,
positionPrice: obj.pp,
reserves: obj.r,
margin: obj.m,
marginUSD: obj.mu,
freeMarginUSD: obj.fmu,
availableWithdrawBalance: obj.awb,
maxAvailableLong: obj.mli,
maxAvailableShort: obj.msi,
leverage: obj.l,
status: obj.s,
}));
const cfdBalancesSchema = z.array(cfdBalanceSchema);
export default cfdBalancesSchema;

View File

@@ -7,5 +7,6 @@ export { default as initMessageSchema } from './initMessageSchema';
export { default as pingPongMessageSchema } from './pingPongMessageSchema';
export { default as swapInfoSchema } from './swapInfoSchema';
export { default as balancesSchema } from './balancesSchema';
export { default as cfdBalancesSchema } from './cfdBalancesSchema';
export * from './orderBookSchema';

View File

@@ -10,6 +10,8 @@ import {
userEarnedSchema,
PairStatusEnum,
pairStatusSchema,
cfdContractsSchema,
cfdHistorySchema,
} from './schemas';
import redeemOrderSchema from '../OrionAggregator/schemas/redeemOrderSchema';
import { sourceAtomicHistorySchema, targetAtomicHistorySchema } from './schemas/atomicHistorySchema';
@@ -52,6 +54,12 @@ type AtomicSwapHistoryTargetQuery = AtomicSwapHistoryBaseQuery & {
expiredRedeem?: 0 | 1,
state?: 'REDEEMED' | 'BEFORE-REDEEM',
}
type CfdHistoryQuery = {
instrument?: string,
page?: number,
limit?: number,
}
class OrionBlockchain {
private readonly apiUrl: string;
@@ -90,6 +98,8 @@ class OrionBlockchain {
this.getBlockNumber = this.getBlockNumber.bind(this);
this.getRedeemOrderBySecretHash = this.getRedeemOrderBySecretHash.bind(this);
this.claimOrder = this.claimOrder.bind(this);
this.getCFDContracts = this.getCFDContracts.bind(this);
this.getCFDHistory = this.getCFDHistory.bind(this);
}
get orionBlockchainWsUrl() {
@@ -175,6 +185,11 @@ class OrionBlockchain {
z.record(z.string()).transform(makePartial),
);
getCFDPrices = () => fetchWithValidation(
`${this.apiUrl}/api/cfd/prices`,
z.record(z.string()).transform(makePartial),
);
getTokensFee = () => fetchWithValidation(
`${this.apiUrl}/api/tokensFee`,
z.record(z.string()).transform(makePartial),
@@ -377,6 +392,20 @@ class OrionBlockchain {
body: JSON.stringify(secretHashes),
},
);
getCFDContracts = () => fetchWithValidation(
`${this.apiUrl}/api/cfd/contracts`,
cfdContractsSchema,
);
getCFDHistory = (address: string, query: CfdHistoryQuery = {}) => {
const url = new URL(`${this.apiUrl}/api/cfd/deposit-withdraw/${address}`);
Object.entries(query)
.forEach(([key, value]) => url.searchParams.append(key, value.toString()));
return fetchWithValidation(url.toString(), cfdHistorySchema);
};
}
export * as schemas from './schemas';

View File

@@ -0,0 +1,19 @@
import { z } from 'zod';
const cfdContractsSchema = z.array(z.object({
name: z.string(),
alias: z.string(),
address: z.string(),
leverage: z.number(),
soLevel: z.number(),
shortFR: z.number(),
longFR: z.number(),
shortFRStored: z.number(),
longFRStored: z.number(),
lastFRPriceUpdateTime: z.number(),
priceIndex: z.number(),
feePercent: z.number(),
withdrawMarginLevel: z.number(),
}));
export default cfdContractsSchema;

View File

@@ -0,0 +1,52 @@
import { z } from 'zod';
import { HistoryTransactionStatus } from '../../../types';
export enum historyTransactionType {
WITHDRAW = 'withdrawal',
DEPOSIT = 'deposit',
}
const cfdHistoryItem = z.object({
_id: z.string(),
__v: z.number(),
address: z.string(),
instrument: z.string(),
instrumentAddress: z.string(),
balance: z.string(),
amount: z.string(),
amountNumber: z.string(),
position: z.string(),
reason: z.enum(['WITHDRAW', 'DEPOSIT']),
positionPrice: z.string(),
fundingRate: z.string(),
transactionHash: z.string(),
blockNumber: z.number(),
createdAt: z.number(),
});
const cfdHistorySchema = z.object({
success: z.boolean(),
count: z.number(),
total: z.number(),
pagination: z.object({}),
data: z.array(cfdHistoryItem),
}).transform((response) => {
return response.data.map((item) => {
const {
createdAt, reason, transactionHash, amountNumber,
} = item;
const type = historyTransactionType[reason];
return {
type,
date: createdAt,
token: 'USDT',
amount: amountNumber,
status: HistoryTransactionStatus.DONE,
transactionHash,
user: item.address,
};
});
});
export default cfdHistorySchema;

View File

@@ -1,4 +1,5 @@
import { z } from 'zod';
import { HistoryTransactionStatus } from '../../../types';
const historySchema = z.array(z.object(
{
@@ -13,6 +14,21 @@ const historySchema = z.array(z.object(
user: z.string(),
walletBalance: z.string().nullable().optional(),
},
));
)).transform((response) => {
return response.map((item) => {
const {
type, createdAt, transactionHash, user,
} = item;
return {
type,
date: createdAt * 1000,
token: item.asset,
amount: item.amountNumber,
status: HistoryTransactionStatus.DONE,
transactionHash,
user,
};
});
});
export default historySchema;

View File

@@ -12,3 +12,5 @@ export { default as atomicSummarySchema } from './atomicSummarySchema';
export { default as poolsLpAndStakedSchema } from './poolsLpAndStakedSchema';
export { default as userVotesSchema } from './userVotesSchema';
export { default as userEarnedSchema } from './userEarnedSchema';
export { default as cfdContractsSchema } from './cfdContractsSchema';
export { default as cfdHistorySchema } from './cfdHistorySchema';

View File

@@ -25,6 +25,27 @@ export type Balance = {
wallet: string,
allowance: string,
}
export type CFDBalance = {
instrument: string,
balance: string,
profitLoss: string,
fundingRate: string,
equity: string,
position: string,
currentPrice: string,
positionPrice: string,
reserves: string,
margin: string,
marginUSD: string,
freeMarginUSD: string,
availableWithdrawBalance: string,
maxAvailableLong: string,
maxAvailableShort: string,
leverage: string,
status: PositionStatus,
}
export interface Order {
senderAddress: string; // address
matcherAddress: string; // address
@@ -39,6 +60,25 @@ export interface Order {
buySide: number; // uint8, 1=buy, 0=sell
isPersonalSign: boolean; // bool
}
export interface CFDOrder {
senderAddress: string; // address
matcherAddress: string; // address
instrumentAddress: string; // address
amount: number; // uint64
price: number; // uint64
matcherFee: number; // uint64
nonce: number; // uint64
expiration: number; // uint64
buySide: number; // uint8, 1=buy, 0=sell
isPersonalSign: boolean; // bool
}
export interface SignedCFDOrder extends CFDOrder {
id: string; // hash of Order (it's not part of order structure in smart-contract)
signature: string; // bytes
}
export interface SignedOrder extends Order {
id: string; // hash of Order (it's not part of order structure in smart-contract)
signature: string; // bytes
@@ -188,3 +228,12 @@ export type SwapInfoByAmountOut = SwapInfoBase & {
}
export type SwapInfo = SwapInfoByAmountIn | SwapInfoByAmountOut;
export enum HistoryTransactionStatus {
PENDING = 'Pending',
DONE = 'Done',
APPROVING = 'Approving',
CANCELLED = 'Cancelled',
}
export type PositionStatus = 'SHORT' | 'LONG' | 'CLOSED' | 'LIQUIDATED' | 'NOT_OPEN';

View File

@@ -3,7 +3,7 @@ type WithReason = {
}
type WithCodeError = Error & {
code: number;
code: number | string;
}
type WithMessage = {
@@ -41,9 +41,9 @@ export function hasProp<T extends Record<string, unknown>, K extends PropertyKey
}
export function isWithCode(candidate: unknown): candidate is WithCodeError {
if (!isUnknownObject(candidate)) return false;
const hasCodeProperty = hasProp(candidate, 'code') && typeof candidate.code === 'number';
return hasCodeProperty;
if (!isUnknownObject(candidate) || !hasProp(candidate, 'code')) return false;
const type = typeof candidate.code;
return type === 'number' || type === 'string';
}
export function isWithReason(candidate: unknown): candidate is WithReason {