mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-03-14 06:02:36 +03:00
New fetchWithValidation / introducing simpleFetch
This commit is contained in:
@@ -15,11 +15,11 @@ module.exports = {
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: "./",
|
||||
tsconfigRootDir: __dirname,
|
||||
project: [
|
||||
"./tsconfig.json"
|
||||
],
|
||||
ecmaVersion: 12,
|
||||
ecmaVersion: 2021,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: [
|
||||
|
||||
49
README.md
49
README.md
@@ -46,7 +46,7 @@ const chain = "0x61"; // bsc-testnet
|
||||
const env = "testing";
|
||||
|
||||
const startApp = async (provider: BaseProvider) => {
|
||||
const web3provider = new providers.Web3Provider(provider);
|
||||
const web3Provider = new providers.Web3Provider(provider);
|
||||
await web3Provider.ready;
|
||||
const signer = web3Provider.getSigner(); // ready to go
|
||||
const orionUnit = initOrionUnit(chain, env); // ready to go
|
||||
@@ -128,7 +128,9 @@ orionUnit.farmingManager.removeAllLiquidity({
|
||||
### Get historical price
|
||||
|
||||
```ts
|
||||
const candles = await orionUnit.priceFeed.getCandles(
|
||||
import { simpleFetch } from "@orionprotocol/sdk";
|
||||
|
||||
const candles = await simpleFetch(orionUnit.priceFeed.getCandles)(
|
||||
"ORN-USDT",
|
||||
1650287678, // interval start
|
||||
1650374078, // interval end
|
||||
@@ -163,13 +165,16 @@ const orionVoting = contracts.OrionVoting__factory.connect(
|
||||
### Get tradable pairs
|
||||
|
||||
```ts
|
||||
const pairsList = await orionUnit.orionAggregator.getPairsList();
|
||||
import { simpleFetch } from "@orionprotocol/sdk";
|
||||
const pairsList = await simpleFetch(orionUnit.orionAggregator.getPairsList)();
|
||||
```
|
||||
|
||||
### Get swap info
|
||||
|
||||
```ts
|
||||
const swapInfo = await orionUnit.orionAggregator.getSwapInfo(
|
||||
import { simpleFetch } from '@orionprotocol/sdk';
|
||||
|
||||
const swapInfo = await simpleFetch(orionUnit.orionAggregator.getSwapInfo)(
|
||||
// Use 'exactSpend' when 'amount' is how much you want spend. Use 'exactReceive' otherwise
|
||||
type: 'exactSpend',
|
||||
assetIn: 'ORN',
|
||||
@@ -181,7 +186,12 @@ const swapInfo = await orionUnit.orionAggregator.getSwapInfo(
|
||||
### Place order in Orion Aggregator
|
||||
|
||||
```ts
|
||||
const { orderId } = await orionUnit.orionAggregator.placeOrder(
|
||||
import { simpleFetch } from "@orionprotocol/sdk";
|
||||
|
||||
// You can yse simpleFetch or "default" (vebose) fetch
|
||||
// Simple fetch
|
||||
|
||||
const { orderId } = await simpleFetch(orionUnit.orionAggregator.placeOrder)(
|
||||
{
|
||||
senderAddress: "0x61eed69c0d112c690fd6f44bb621357b89fbe67f",
|
||||
matcherAddress: "0xfbcad2c3a90fbd94c335fbdf8e22573456da7f68",
|
||||
@@ -198,6 +208,35 @@ const { orderId } = await orionUnit.orionAggregator.placeOrder(
|
||||
},
|
||||
false // Place in internal orderbook
|
||||
);
|
||||
|
||||
// Default ("verbose") fetch
|
||||
|
||||
const placeOrderFetchResult = await simpleFetch(
|
||||
orionUnit.orionAggregator.placeOrder
|
||||
)();
|
||||
// Same params as above
|
||||
|
||||
if (placeOrderFetchResult.isErr()) {
|
||||
// You can handle fetching errors here
|
||||
// You can access error text, statuses
|
||||
const { error } = placeOrderFetchResult;
|
||||
switch (error.type) {
|
||||
case "fetchError": // (no network, connection refused, connection break)
|
||||
console.error(error.message);
|
||||
break;
|
||||
case "unknownFetchError": // Instance of Error
|
||||
console.error(`URL: ${error.url}, Error: ${error.message}`);
|
||||
break;
|
||||
case "unknownFetchThrow":
|
||||
console.error("Something wrong happened furing fetching", error.error);
|
||||
break;
|
||||
// ... and 8 errors types more
|
||||
// see src/fetchWithValidation.ts for details
|
||||
}
|
||||
} else {
|
||||
// Succes result
|
||||
const { orderId } = placeOrderFetchResult.value;
|
||||
}
|
||||
```
|
||||
|
||||
### Orion Aggregator WebSocket
|
||||
|
||||
11
package-lock.json
generated
11
package-lock.json
generated
@@ -18,6 +18,7 @@
|
||||
"ethers": "^5.6.2",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"just-clone": "^5.0.1",
|
||||
"neverthrow": "^4.3.1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"socket.io-client": "2.4.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
@@ -9135,6 +9136,11 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/neverthrow": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/neverthrow/-/neverthrow-4.3.1.tgz",
|
||||
"integrity": "sha512-+vxjSaiDWjAj6kR6KKW0YDuV6O4UCNWGAO8m8ITjFKPWcTmU1GVnL+J5TAUTKpPnUAHCKDxXpOHVaERid223Ww=="
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
@@ -17497,6 +17503,11 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"neverthrow": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/neverthrow/-/neverthrow-4.3.1.tgz",
|
||||
"integrity": "sha512-+vxjSaiDWjAj6kR6KKW0YDuV6O4UCNWGAO8m8ITjFKPWcTmU1GVnL+J5TAUTKpPnUAHCKDxXpOHVaERid223Ww=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@orionprotocol/sdk",
|
||||
"version": "0.3.4",
|
||||
"version": "0.4.0",
|
||||
"description": "Orion Protocol SDK",
|
||||
"main": "./lib/esm/index.js",
|
||||
"module": "./lib/esm/index.js",
|
||||
@@ -70,6 +70,7 @@
|
||||
"ethers": "^5.6.2",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"just-clone": "^5.0.1",
|
||||
"neverthrow": "^4.3.1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"socket.io-client": "2.4.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from '../../constants';
|
||||
import { normalizeNumber } from '../../utils';
|
||||
import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency';
|
||||
import simpleFetch from '../../simpleFetch';
|
||||
|
||||
export type DepositParams = {
|
||||
asset: string,
|
||||
@@ -38,12 +39,12 @@ export default async function deposit({
|
||||
const {
|
||||
exchangeContractAddress,
|
||||
assetToAddress,
|
||||
} = await orionBlockchain.getInfo();
|
||||
} = await simpleFetch(orionBlockchain.getInfo)();
|
||||
|
||||
const nativeCryptocurrency = getNativeCryptocurrency(assetToAddress);
|
||||
|
||||
const exchangeContract = contracts.Exchange__factory.connect(exchangeContractAddress, provider);
|
||||
const gasPriceWei = await orionBlockchain.getGasPriceWei();
|
||||
const gasPriceWei = await simpleFetch(orionBlockchain.getGasPriceWei)();
|
||||
|
||||
const assetAddress = assetToAddress[asset];
|
||||
if (!assetAddress) throw new Error(`Asset '${asset}' not found`);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable max-len */
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { ethers } from 'ethers';
|
||||
@@ -8,6 +9,7 @@ import OrionUnit from '..';
|
||||
import { contracts, crypt, utils } from '../..';
|
||||
import { INTERNAL_ORION_PRECISION, NATIVE_CURRENCY_PRECISION, SWAP_THROUGH_ORION_POOL_GAS_LIMIT } from '../../constants';
|
||||
import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency';
|
||||
import simpleFetch from '../../simpleFetch';
|
||||
|
||||
export type SwapMarketParams = {
|
||||
type: 'exactSpend' | 'exactReceive',
|
||||
@@ -74,13 +76,13 @@ export default async function swapMarket({
|
||||
exchangeContractAddress,
|
||||
matcherAddress,
|
||||
assetToAddress,
|
||||
} = await orionBlockchain.getInfo();
|
||||
const nativeCryptocurrency = getNativeCryptocurrency(assetToAddress);
|
||||
} = await simpleFetch(orionBlockchain.getInfo)();
|
||||
const nativeCryptocurrency = getNativeCryptocurrency(assetToAddress);
|
||||
|
||||
const exchangeContract = contracts.Exchange__factory.connect(exchangeContractAddress, provider);
|
||||
const feeAssets = await orionBlockchain.getTokensFee();
|
||||
const pricesInOrn = await orionBlockchain.getPrices();
|
||||
const gasPriceWei = await orionBlockchain.getGasPriceWei();
|
||||
const feeAssets = await simpleFetch(orionBlockchain.getTokensFee)();
|
||||
const pricesInOrn = await simpleFetch(orionBlockchain.getPrices)();
|
||||
const gasPriceWei = await simpleFetch(orionBlockchain.getGasPriceWei)();
|
||||
|
||||
const gasPriceGwei = ethers.utils.formatUnits(gasPriceWei, 'gwei').toString();
|
||||
|
||||
@@ -111,7 +113,7 @@ export default async function swapMarket({
|
||||
signer,
|
||||
);
|
||||
|
||||
const swapInfo = await orionAggregator.getSwapInfo(type, assetIn, assetOut, amount.toString());
|
||||
const swapInfo = await simpleFetch(orionAggregator.getSwapInfo)(type, assetIn, assetOut, amount.toString());
|
||||
|
||||
if (swapInfo.type === 'exactReceive' && amountBN.lt(swapInfo.minAmountOut)) {
|
||||
throw new Error(`Amount is too low. Min amountOut is ${swapInfo.minAmountOut} ${assetOut}`);
|
||||
@@ -240,7 +242,7 @@ export default async function swapMarket({
|
||||
.toString();
|
||||
|
||||
const [baseAssetName, quoteAssetName] = swapInfo.orderInfo.assetPair.split('-');
|
||||
const pairConfig = await orionAggregator.getPairConfig(`${baseAssetName}-${quoteAssetName}`);
|
||||
const pairConfig = await simpleFetch(orionAggregator.getPairConfig)(`${baseAssetName}-${quoteAssetName}`);
|
||||
if (!pairConfig) throw new Error(`Pair config ${baseAssetName}-${quoteAssetName} not found`);
|
||||
|
||||
const baseAssetAddress = assetToAddress[baseAssetName];
|
||||
@@ -336,7 +338,7 @@ export default async function swapMarket({
|
||||
const orderIsOk = await exchangeContract.validateOrder(signedOrder);
|
||||
if (!orderIsOk) throw new Error('Order is not valid');
|
||||
|
||||
const { orderId } = await orionAggregator.placeOrder(signedOrder, false);
|
||||
const { orderId } = await simpleFetch(orionAggregator.placeOrder)(signedOrder, false);
|
||||
return {
|
||||
through: 'aggregator',
|
||||
id: orderId,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from '../../constants';
|
||||
import { normalizeNumber } from '../../utils';
|
||||
import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency';
|
||||
import simpleFetch from '../../simpleFetch';
|
||||
|
||||
export type WithdrawParams = {
|
||||
asset: string,
|
||||
@@ -38,11 +39,11 @@ export default async function withdraw({
|
||||
const {
|
||||
exchangeContractAddress,
|
||||
assetToAddress,
|
||||
} = await orionBlockchain.getInfo();
|
||||
} = await simpleFetch(orionBlockchain.getInfo)();
|
||||
|
||||
const nativeCryptocurrency = getNativeCryptocurrency(assetToAddress);
|
||||
const exchangeContract = contracts.Exchange__factory.connect(exchangeContractAddress, provider);
|
||||
const gasPriceWei = await orionBlockchain.getGasPriceWei();
|
||||
const gasPriceWei = await simpleFetch(orionBlockchain.getGasPriceWei)();
|
||||
|
||||
const assetAddress = assetToAddress[asset];
|
||||
if (!assetAddress) throw new Error(`Asset '${asset}' not found`);
|
||||
|
||||
@@ -4,6 +4,7 @@ import OrionUnit from '..';
|
||||
import { contracts } from '../..';
|
||||
import BalanceGuard from '../../BalanceGuard';
|
||||
import { ADD_LIQUIDITY_GAS_LIMIT, INTERNAL_ORION_PRECISION, NATIVE_CURRENCY_PRECISION } from '../../constants';
|
||||
import simpleFetch from '../../simpleFetch';
|
||||
import { denormalizeNumber, normalizeNumber } from '../../utils';
|
||||
import getBalances from '../../utils/getBalances';
|
||||
import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency';
|
||||
@@ -46,7 +47,7 @@ export default class FarmingManager {
|
||||
exchangeContractAddress,
|
||||
assetToAddress,
|
||||
assetToDecimals,
|
||||
} = await this.orionUnit.orionBlockchain.getInfo();
|
||||
} = await simpleFetch(this.orionUnit.orionBlockchain.getInfo)();
|
||||
|
||||
const walletAddress = await signer.getAddress();
|
||||
|
||||
@@ -85,7 +86,7 @@ export default class FarmingManager {
|
||||
signer,
|
||||
);
|
||||
|
||||
const poolsConfig = await this.orionUnit.orionBlockchain.getPoolsConfig();
|
||||
const poolsConfig = await simpleFetch(this.orionUnit.orionBlockchain.getPoolsConfig)();
|
||||
const pool = poolsConfig.pools[poolName];
|
||||
if (!pool) throw new Error(`Pool ${poolName} not found`);
|
||||
|
||||
@@ -223,7 +224,7 @@ export default class FarmingManager {
|
||||
assetToAddress,
|
||||
assetToDecimals,
|
||||
exchangeContractAddress,
|
||||
} = await this.orionUnit.orionBlockchain.getInfo();
|
||||
} = await simpleFetch(this.orionUnit.orionBlockchain.getInfo)();
|
||||
|
||||
const assetAAddress = assetToAddress[assetA];
|
||||
if (!assetAAddress) throw new Error(`Asset '${assetA}' not found`);
|
||||
@@ -235,7 +236,7 @@ export default class FarmingManager {
|
||||
const assetBDecimals = assetToDecimals[assetB];
|
||||
if (!assetBDecimals) throw new Error(`Decimals for asset '${assetB}' not found`);
|
||||
|
||||
const poolsConfig = await this.orionUnit.orionBlockchain.getPoolsConfig();
|
||||
const poolsConfig = await simpleFetch(this.orionUnit.orionBlockchain.getPoolsConfig)();
|
||||
const pool = poolsConfig.pools[poolName];
|
||||
if (!pool) throw new Error(`Pool ${poolName} not found`);
|
||||
|
||||
|
||||
@@ -1,55 +1,155 @@
|
||||
import { Schema, z } from 'zod';
|
||||
import fetch, { RequestInit } from 'node-fetch';
|
||||
import { isWithError, isWithReason, HttpError } from './utils';
|
||||
import fetch, { FetchError, RequestInit } from 'node-fetch';
|
||||
import {
|
||||
err, fromPromise, fromThrowable, ok,
|
||||
} from 'neverthrow';
|
||||
|
||||
export class ExtendedError extends Error {
|
||||
public url: string;
|
||||
|
||||
public status: number | null;
|
||||
|
||||
constructor(url: string, status: number | null, message: string) {
|
||||
super();
|
||||
this.url = url;
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchJsonWithValidation = async <DataOut, DataIn, ErrorOut, ErrorIn>(
|
||||
export default async function fetchWithValidation<DataOut, DataIn, ErrorOut, ErrorIn>(
|
||||
url: string,
|
||||
schema: Schema<DataOut, z.ZodTypeDef, DataIn>,
|
||||
options?: RequestInit,
|
||||
errorSchema?: Schema<ErrorOut, z.ZodTypeDef, ErrorIn>,
|
||||
) => {
|
||||
const response = await fetch(url, {
|
||||
) {
|
||||
// Cases:
|
||||
// 1. fetchError (no network, connection refused, connection break)
|
||||
// 2. unknownFetchError
|
||||
// 3. unknownFetchThrow
|
||||
// 4. unknownGetTextError
|
||||
// 5. unknownGetTextUnknownError
|
||||
// 6. serverError
|
||||
// 7. jsonParseError
|
||||
// 8. jsonParseUnknownError
|
||||
// 9. clientErrorWithResponsePayload
|
||||
// 10. clientErrorPayloadParseError
|
||||
// 11. clientError
|
||||
// 12. payloadParseError
|
||||
// 13. payload
|
||||
|
||||
const fetchResult = await fromPromise(fetch(url, {
|
||||
...options || {},
|
||||
headers: {
|
||||
'Cache-Control': 'no-store, max-age=0',
|
||||
...(options ? options.headers : {}),
|
||||
},
|
||||
});
|
||||
const text = await response.text();
|
||||
|
||||
// The ok read-only property of the Response interface contains a Boolean
|
||||
// stating whether the response was successful (status in the range 200 - 299) or not.
|
||||
|
||||
if (!response.ok) {
|
||||
throw new HttpError(response.status, text, 'HTTP', response.statusText);
|
||||
}
|
||||
|
||||
const payload: unknown = JSON.parse(text);
|
||||
|
||||
try {
|
||||
const data = schema.parse(payload);
|
||||
return data;
|
||||
} catch (e) {
|
||||
if (errorSchema) {
|
||||
const errorObj = errorSchema.parse(payload);
|
||||
if (isWithError(errorObj) && isWithReason(errorObj.error)) {
|
||||
throw new ExtendedError(url, response.status, errorObj.error.reason);
|
||||
}
|
||||
}), (e) => {
|
||||
if (e instanceof FetchError) {
|
||||
return err({
|
||||
type: 'fetchError' as const,
|
||||
url,
|
||||
message: `${e.message} (${e.type})`,
|
||||
error: e,
|
||||
});
|
||||
} if (e instanceof Error) {
|
||||
return err({
|
||||
type: 'unknownFetchError' as const,
|
||||
url,
|
||||
message: e.message,
|
||||
error: e,
|
||||
});
|
||||
}
|
||||
if (e instanceof Error) throw new ExtendedError(url, response.status, e.message);
|
||||
throw e;
|
||||
return err({
|
||||
type: 'unknownFetchThrow' as const,
|
||||
url,
|
||||
message: 'Unknown fetch error',
|
||||
error: e,
|
||||
});
|
||||
});
|
||||
|
||||
if (fetchResult.isErr()) return fetchResult.error;
|
||||
|
||||
const response = fetchResult.value;
|
||||
|
||||
const textResult = await fromPromise(response.text(), (e) => {
|
||||
if (e instanceof Error) {
|
||||
return err({
|
||||
type: 'unknownGetTextError' as const,
|
||||
url,
|
||||
message: `Can't get response content: ${e.message}`,
|
||||
error: e,
|
||||
});
|
||||
}
|
||||
return err({
|
||||
type: 'unknownGetTextUnknownError' as const,
|
||||
url,
|
||||
message: "Can't get response content: unknown error",
|
||||
error: e,
|
||||
});
|
||||
});
|
||||
|
||||
if (textResult.isErr()) return textResult.error;
|
||||
|
||||
const text = textResult.value;
|
||||
|
||||
if (response.status >= 500) { // Server error
|
||||
return err({
|
||||
type: 'serverError' as const,
|
||||
url,
|
||||
message: `Server error: ${response.status} ${response.statusText}`,
|
||||
status: response.status,
|
||||
text,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const safeParseJson = fromThrowable(JSON.parse, (e) => {
|
||||
if (e instanceof Error) {
|
||||
return err({
|
||||
type: 'jsonParseError' as const,
|
||||
url,
|
||||
message: e.message,
|
||||
error: e,
|
||||
});
|
||||
}
|
||||
return err({
|
||||
type: 'jsonParseUnknownError' as const,
|
||||
url,
|
||||
message: 'Unknown JSON parse error',
|
||||
error: e,
|
||||
});
|
||||
});
|
||||
|
||||
const jsonResult = safeParseJson(text);
|
||||
|
||||
if (jsonResult.isErr()) return jsonResult.error;
|
||||
|
||||
const json: unknown = jsonResult.value;
|
||||
|
||||
if (response.status >= 400) { // Client error
|
||||
if (errorSchema) {
|
||||
const serverError = errorSchema.safeParse(json);
|
||||
if (serverError.success) {
|
||||
return err({
|
||||
type: 'clientErrorWithResponsePayload' as const,
|
||||
url,
|
||||
message: `Client error: ${response.status} ${response.statusText}`,
|
||||
status: response.status,
|
||||
payload: serverError.data,
|
||||
});
|
||||
}
|
||||
return err({
|
||||
type: 'clientErrorPayloadParseError' as const,
|
||||
message: 'Can\'t recognize error message',
|
||||
status: response.status,
|
||||
text,
|
||||
error: serverError.error,
|
||||
});
|
||||
}
|
||||
return err({
|
||||
type: 'clientError' as const,
|
||||
url,
|
||||
message: `Error: ${response.status} ${response.statusText}`,
|
||||
status: response.status,
|
||||
text,
|
||||
});
|
||||
}
|
||||
|
||||
const payload = schema.safeParse(json);
|
||||
if (!payload.success) {
|
||||
return err({
|
||||
type: 'payloadParseError' as const,
|
||||
url,
|
||||
message: 'Can\'t recognize response payload',
|
||||
error: payload.error,
|
||||
});
|
||||
}
|
||||
return ok(payload.data);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ export * as config from './config';
|
||||
// export * from './entities';
|
||||
export { default as OrionUnit } from './OrionUnit';
|
||||
export { default as initOrionUnit } from './initOrionUnit';
|
||||
export * from './fetchWithValidation';
|
||||
export { default as fetchWithValidation } from './fetchWithValidation';
|
||||
export { default as simpleFetch } from './simpleFetch';
|
||||
export * as utils from './utils';
|
||||
export * as services from './services';
|
||||
export * as contracts from './artifacts/contracts';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { z } from 'zod';
|
||||
import { fetchJsonWithValidation } from '../../fetchWithValidation';
|
||||
import fetchWithValidation from '../../fetchWithValidation';
|
||||
import swapInfoSchema from './schemas/swapInfoSchema';
|
||||
import exchangeInfoSchema from './schemas/exchangeInfoSchema';
|
||||
import cancelOrderSchema from './schemas/cancelOrderSchema';
|
||||
@@ -8,7 +8,7 @@ import orderBenefitsSchema from './schemas/orderBenefitsSchema';
|
||||
import errorSchema from './errorSchema';
|
||||
import placeAtomicSwapSchema from './schemas/placeAtomicSwapSchema';
|
||||
import { OrionAggregatorWS } from './ws';
|
||||
import atomicSwapHistorySchema from './schemas/atomicSwapHistorySchema';
|
||||
import { atomicSwapHistorySchema } from './schemas/atomicSwapHistorySchema';
|
||||
import { SignedCancelOrderRequest, SignedOrder, SupportedChainId } from '../../types';
|
||||
import { pairConfigSchema } from './schemas';
|
||||
|
||||
@@ -20,6 +20,18 @@ class OrionAggregator {
|
||||
constructor(apiUrl: string, chainId: SupportedChainId) {
|
||||
this.apiUrl = apiUrl;
|
||||
this.ws = new OrionAggregatorWS(this.aggregatorWSUrl, chainId);
|
||||
|
||||
this.getHistoryAtomicSwaps = this.getHistoryAtomicSwaps.bind(this);
|
||||
this.getPairConfig = this.getPairConfig.bind(this);
|
||||
this.getPairConfigs = this.getPairConfigs.bind(this);
|
||||
this.getPairsList = this.getPairsList.bind(this);
|
||||
this.getSwapInfo = this.getSwapInfo.bind(this);
|
||||
this.getTradeProfits = this.getTradeProfits.bind(this);
|
||||
this.placeAtomicSwap = this.placeAtomicSwap.bind(this);
|
||||
this.placeOrder = this.placeOrder.bind(this);
|
||||
this.cancelOrder = this.cancelOrder.bind(this);
|
||||
this.checkWhitelisted = this.checkWhitelisted.bind(this);
|
||||
this.getLockedBalance = this.getLockedBalance.bind(this);
|
||||
}
|
||||
|
||||
get aggregatorWSUrl() { return `wss://${this.apiUrl}/v1`; }
|
||||
@@ -29,14 +41,14 @@ class OrionAggregator {
|
||||
}
|
||||
|
||||
getPairsList() {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`${this.aggregatorUrl}/api/v1/pairs/list`,
|
||||
z.array(z.string()),
|
||||
);
|
||||
}
|
||||
|
||||
getPairConfigs() {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`${this.aggregatorUrl}/api/v1/pairs/exchangeInfo`,
|
||||
exchangeInfoSchema,
|
||||
undefined,
|
||||
@@ -45,7 +57,7 @@ class OrionAggregator {
|
||||
}
|
||||
|
||||
getPairConfig(assetPair: string) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`${this.aggregatorUrl}/api/v1/pairs/exchangeInfo/${assetPair}`,
|
||||
pairConfigSchema,
|
||||
undefined,
|
||||
@@ -54,7 +66,7 @@ class OrionAggregator {
|
||||
}
|
||||
|
||||
checkWhitelisted(address: string) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`${this.aggregatorUrl}/api/v1/whitelist/check?address=${address}`,
|
||||
z.boolean(),
|
||||
undefined,
|
||||
@@ -73,7 +85,7 @@ class OrionAggregator {
|
||||
...partnerId && { 'X-Partner-Id': partnerId },
|
||||
};
|
||||
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`${this.aggregatorUrl}/api/v1/order/${isCreateInternalOrder ? 'internal' : ''}`,
|
||||
z.object({
|
||||
orderId: z.string(),
|
||||
@@ -95,7 +107,7 @@ class OrionAggregator {
|
||||
}
|
||||
|
||||
cancelOrder(signedCancelOrderRequest: SignedCancelOrderRequest) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`${this.aggregatorUrl}/api/v1/order`,
|
||||
cancelOrderSchema,
|
||||
{
|
||||
@@ -128,7 +140,7 @@ class OrionAggregator {
|
||||
url.searchParams.append('amountOut', amount);
|
||||
}
|
||||
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
url.toString(),
|
||||
swapInfoSchema,
|
||||
undefined,
|
||||
@@ -139,7 +151,7 @@ class OrionAggregator {
|
||||
getLockedBalance(address: string, currency: string) {
|
||||
const url = new URL(`${this.aggregatorUrl}/api/v1/address/balance/reserved/${currency}`);
|
||||
url.searchParams.append('address', address);
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
url.toString(),
|
||||
z.object({
|
||||
[currency]: z.number(),
|
||||
@@ -159,7 +171,7 @@ class OrionAggregator {
|
||||
url.searchParams.append('amount', amount.toString());
|
||||
url.searchParams.append('side', isBuy ? 'buy' : 'sell');
|
||||
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
url.toString(),
|
||||
orderBenefitsSchema,
|
||||
undefined,
|
||||
@@ -177,7 +189,7 @@ class OrionAggregator {
|
||||
secretHash: string,
|
||||
sourceNetworkCode: string,
|
||||
) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`${this.aggregatorUrl}/api/v1/atomic-swap`,
|
||||
placeAtomicSwapSchema,
|
||||
{
|
||||
@@ -204,7 +216,7 @@ class OrionAggregator {
|
||||
const url = new URL(`${this.aggregatorUrl}/api/v1/atomic-swap/history/all`);
|
||||
url.searchParams.append('sender', sender);
|
||||
url.searchParams.append('limit', limit.toString());
|
||||
return fetchJsonWithValidation(url.toString(), atomicSwapHistorySchema);
|
||||
return fetchWithValidation(url.toString(), atomicSwapHistorySchema);
|
||||
}
|
||||
}
|
||||
export * as schemas from './schemas';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { fetchJsonWithValidation } from '../../fetchWithValidation';
|
||||
import fetchWithValidation from '../../fetchWithValidation';
|
||||
import { PairStatusEnum, pairStatusSchema } from './schemas/adminPoolsListSchema';
|
||||
import {
|
||||
IDOSchema, atomicHistorySchema,
|
||||
@@ -56,6 +56,32 @@ class OrionBlockchain {
|
||||
constructor(apiUrl: string) {
|
||||
this.apiUrl = apiUrl;
|
||||
this.ws = new OrionBlockchainSocketIO(`https://${apiUrl}/`);
|
||||
|
||||
this.getAtomicSwapAssets = this.getAtomicSwapAssets.bind(this);
|
||||
this.getAtomicSwapHistory = this.getAtomicSwapHistory.bind(this);
|
||||
this.getAuthToken = this.getAuthToken.bind(this);
|
||||
this.getCirculatingSupply = this.getCirculatingSupply.bind(this);
|
||||
this.getInfo = this.getInfo.bind(this);
|
||||
this.getPoolsConfig = this.getPoolsConfig.bind(this);
|
||||
this.getPoolsInfo = this.getPoolsInfo.bind(this);
|
||||
this.getHistory = this.getHistory.bind(this);
|
||||
this.getPrices = this.getPrices.bind(this);
|
||||
this.getTokensFee = this.getTokensFee.bind(this);
|
||||
this.getGasPriceWei = this.getGasPriceWei.bind(this);
|
||||
this.checkFreeRedeemAvailable = this.checkFreeRedeemAvailable.bind(this);
|
||||
this.redeemAtomicSwap = this.redeemAtomicSwap.bind(this);
|
||||
this.redeem2AtomicSwaps = this.redeem2AtomicSwaps.bind(this);
|
||||
this.checkRedeem = this.checkRedeem.bind(this);
|
||||
this.checkRedeem2Atomics = this.checkRedeem2Atomics.bind(this);
|
||||
this.getIDOInfo = this.getIDOInfo.bind(this);
|
||||
this.checkAuth = this.checkAuth.bind(this);
|
||||
this.addPool = this.addPool.bind(this);
|
||||
this.editPool = this.editPool.bind(this);
|
||||
this.getPoolsList = this.getPoolsList.bind(this);
|
||||
this.getSourceAtomicSwapHistory = this.getSourceAtomicSwapHistory.bind(this);
|
||||
this.getTargetAtomicSwapHistory = this.getTargetAtomicSwapHistory.bind(this);
|
||||
this.checkPoolInformation = this.checkPoolInformation.bind(this);
|
||||
this.checkIfHashUsed = this.checkIfHashUsed.bind(this);
|
||||
}
|
||||
|
||||
get orionBlockchainWsUrl() {
|
||||
@@ -63,43 +89,43 @@ class OrionBlockchain {
|
||||
}
|
||||
|
||||
getAuthToken() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/auth/token`, z.object({ token: z.string() }));
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/auth/token`, z.object({ token: z.string() }));
|
||||
}
|
||||
|
||||
getCirculatingSupply() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/circulating-supply`, z.number());
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/circulating-supply`, z.number());
|
||||
}
|
||||
|
||||
getInfo() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/info`, infoSchema);
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/info`, infoSchema);
|
||||
}
|
||||
|
||||
getPoolsConfig() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/pools/config`, poolsConfigSchema);
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/pools/config`, poolsConfigSchema);
|
||||
}
|
||||
|
||||
getPoolsInfo() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/pools/info`, poolsInfoSchema);
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/pools/info`, poolsInfoSchema);
|
||||
}
|
||||
|
||||
getHistory(address: string) {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/history/${address}`, historySchema);
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/history/${address}`, historySchema);
|
||||
}
|
||||
|
||||
getPrices() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/prices`, z.record(z.string()).transform(utils.makePartial));
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/prices`, z.record(z.string()).transform(utils.makePartial));
|
||||
}
|
||||
|
||||
getTokensFee() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/tokensFee`, z.record(z.string()).transform(utils.makePartial));
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/tokensFee`, z.record(z.string()).transform(utils.makePartial));
|
||||
}
|
||||
|
||||
getGasPriceWei() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/gasPrice`, z.string());
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/gasPrice`, z.string());
|
||||
}
|
||||
|
||||
checkFreeRedeemAvailable(walletAddress: string) {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/atomic/has-free-redeem/${walletAddress}`, z.boolean());
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/atomic/has-free-redeem/${walletAddress}`, z.boolean());
|
||||
}
|
||||
|
||||
redeemAtomicSwap(
|
||||
@@ -107,7 +133,7 @@ class OrionBlockchain {
|
||||
secret: string,
|
||||
sourceNetwork: string,
|
||||
) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`https://${this.apiUrl}/api/atomic/matcher-redeem`,
|
||||
z.string(),
|
||||
{
|
||||
@@ -131,7 +157,7 @@ class OrionBlockchain {
|
||||
secret2: string,
|
||||
sourceNetwork: string,
|
||||
) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`https://${this.apiUrl}/api/atomic/matcher-redeem2atomics`,
|
||||
z.string(),
|
||||
{
|
||||
@@ -151,31 +177,31 @@ class OrionBlockchain {
|
||||
}
|
||||
|
||||
checkRedeem(secretHash: string) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`https://${this.apiUrl}/api/atomic/matcher-redeem/${secretHash}`,
|
||||
z.enum(['OK', 'FAIL']).nullable(),
|
||||
);
|
||||
}
|
||||
|
||||
checkRedeem2Atomics(firstSecretHash: string, secondSecretHash: string) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`https://${this.apiUrl}/api/atomic/matcher-redeem/${firstSecretHash}-${secondSecretHash}`,
|
||||
z.enum(['OK', 'FAIL']).nullable(),
|
||||
);
|
||||
}
|
||||
|
||||
getIDOInfo() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/solarflare`, IDOSchema);
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/solarflare`, IDOSchema);
|
||||
}
|
||||
|
||||
checkAuth(headers: IAdminAuthHeaders) {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/auth/check`, z.object({
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/auth/check`, z.object({
|
||||
auth: z.boolean(),
|
||||
}), { headers });
|
||||
}
|
||||
|
||||
getPoolsList(headers: IAdminAuthHeaders) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`https://${this.apiUrl}/api/pools/list`,
|
||||
adminPoolsListSchema,
|
||||
{ headers },
|
||||
@@ -183,7 +209,7 @@ class OrionBlockchain {
|
||||
}
|
||||
|
||||
editPool(address: string, data: IEditPool, headers: IAdminAuthHeaders) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`https://${this.apiUrl}/api/pools/edit/${address}`,
|
||||
pairStatusSchema,
|
||||
{
|
||||
@@ -198,22 +224,27 @@ class OrionBlockchain {
|
||||
}
|
||||
|
||||
addPool(data: z.infer<typeof addPoolSchema>) {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/pools/add`, z.number(), {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
return fetchWithValidation(
|
||||
`https://${this.apiUrl}/api/pools/add`,
|
||||
z.number(),
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
},
|
||||
});
|
||||
z.string(),
|
||||
);
|
||||
}
|
||||
|
||||
checkPoolInformation(poolAddress: string) {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/pools/check/${poolAddress}`, pairStatusSchema);
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/pools/check/${poolAddress}`, pairStatusSchema);
|
||||
}
|
||||
|
||||
getAtomicSwapAssets() {
|
||||
return fetchJsonWithValidation(`https://${this.apiUrl}/api/atomic/swap-assets`, z.array(z.string()));
|
||||
return fetchWithValidation(`https://${this.apiUrl}/api/atomic/swap-assets`, z.array(z.string()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +257,7 @@ class OrionBlockchain {
|
||||
Object.entries(query)
|
||||
.forEach(([key, value]) => url.searchParams.append(key, value.toString()));
|
||||
|
||||
return fetchJsonWithValidation(url.toString(), atomicHistorySchema);
|
||||
return fetchWithValidation(url.toString(), atomicHistorySchema);
|
||||
}
|
||||
|
||||
getSourceAtomicSwapHistory(query: AtomicSwapHistorySourceQuery) {
|
||||
@@ -237,7 +268,7 @@ class OrionBlockchain {
|
||||
|
||||
if (!query.type) url.searchParams.append('type', 'source');
|
||||
|
||||
return fetchJsonWithValidation(url.toString(), sourceAtomicHistorySchema);
|
||||
return fetchWithValidation(url.toString(), sourceAtomicHistorySchema);
|
||||
}
|
||||
|
||||
getTargetAtomicSwapHistory(query: AtomicSwapHistoryTargetQuery) {
|
||||
@@ -248,11 +279,11 @@ class OrionBlockchain {
|
||||
|
||||
if (!query.type) url.searchParams.append('type', 'target');
|
||||
|
||||
return fetchJsonWithValidation(url.toString(), targetAtomicHistorySchema);
|
||||
return fetchWithValidation(url.toString(), targetAtomicHistorySchema);
|
||||
}
|
||||
|
||||
checkIfHashUsed(secretHashes: string[]) {
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
`https://${this.apiUrl}/api/atomic/is-hash-used`,
|
||||
z.record(z.boolean()).transform(utils.makePartial),
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fetchJsonWithValidation } from '../../fetchWithValidation';
|
||||
import fetchWithValidation from '../../fetchWithValidation';
|
||||
import candlesSchema from './schemas/candlesSchema';
|
||||
|
||||
class PriceFeed {
|
||||
@@ -6,6 +6,8 @@ class PriceFeed {
|
||||
|
||||
constructor(apiUrl: string) {
|
||||
this.apiUrl = apiUrl;
|
||||
|
||||
this.getCandles = this.getCandles.bind(this);
|
||||
}
|
||||
|
||||
getCandles(
|
||||
@@ -22,7 +24,7 @@ class PriceFeed {
|
||||
url.searchParams.append('interval', interval);
|
||||
url.searchParams.append('exchange', exchange);
|
||||
|
||||
return fetchJsonWithValidation(
|
||||
return fetchWithValidation(
|
||||
url.toString(),
|
||||
candlesSchema,
|
||||
);
|
||||
|
||||
28
src/simpleFetch.ts
Normal file
28
src/simpleFetch.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Schema, z } from 'zod';
|
||||
import { RequestInit } from 'node-fetch';
|
||||
import fetchWithValidation from './fetchWithValidation';
|
||||
|
||||
// https://stackoverflow.com/a/64919133
|
||||
class Wrapper<DataOut, DataIn, ErrorOut, ErrorIn> {
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
wrapped(
|
||||
url: string,
|
||||
schema: Schema<DataOut, z.ZodTypeDef, DataIn>,
|
||||
options?: RequestInit,
|
||||
errorSchema?: Schema<ErrorOut, z.ZodTypeDef, ErrorIn>,
|
||||
) {
|
||||
return fetchWithValidation<DataOut, DataIn, ErrorOut, ErrorIn>(url, schema, options, errorSchema);
|
||||
}
|
||||
}
|
||||
|
||||
type FetchWithValidationInternalType<O, I, EO, EI> = ReturnType<Wrapper<O, I, EO, EI>['wrapped']>
|
||||
|
||||
export default function simpleFetch<O, I, EO, EI, P extends unknown[]>(
|
||||
f: (...params: P) => FetchWithValidationInternalType<O, I, EO, EI>,
|
||||
) {
|
||||
return async (...params: Parameters<typeof f>) => {
|
||||
const result = await f(...params);
|
||||
if (result.isErr()) throw new Error(result.error.message);
|
||||
return result.value;
|
||||
};
|
||||
}
|
||||
@@ -30,10 +30,13 @@ export default async function getBalance(
|
||||
}
|
||||
const assetContractBalance = await exchangeContract.getBalance(assetAddress, walletAddress);
|
||||
const denormalizedAssetInContractBalance = utils.denormalizeNumber(assetContractBalance, INTERNAL_ORION_PRECISION);
|
||||
const denormalizedAssetLockedBalance = await orionAggregator.getLockedBalance(walletAddress, asset);
|
||||
const denormalizedAssetLockedBalanceResult = await orionAggregator.getLockedBalance(walletAddress, asset);
|
||||
if (denormalizedAssetLockedBalanceResult.isErr()) {
|
||||
throw new Error(denormalizedAssetLockedBalanceResult.error.message);
|
||||
}
|
||||
|
||||
return {
|
||||
exchange: denormalizedAssetInContractBalance.minus(denormalizedAssetLockedBalance[asset] ?? 0),
|
||||
exchange: denormalizedAssetInContractBalance.minus(denormalizedAssetLockedBalanceResult.value[asset] ?? 0),
|
||||
wallet: denormalizedAssetInWalletBalance,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ 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 { default as HttpError } from './httpError';
|
||||
|
||||
export * from './typeHelpers';
|
||||
|
||||
Reference in New Issue
Block a user