mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-03-14 06:02:36 +03:00
Orion, Orion Unit, Configuration (#40)
* Refactoring * Better docs * Bump * ESLint standard * Fix * Bumo * VerboseOrionUnitConfig to types * Docs improvements * Docs improvements. Orion default env
This commit is contained in:
@@ -6,7 +6,7 @@ module.exports = {
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
'airbnb-base',
|
||||
'standard',
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
@@ -26,6 +26,10 @@ module.exports = {
|
||||
'@typescript-eslint',
|
||||
],
|
||||
rules: {
|
||||
"comma-dangle": 0,
|
||||
"semi": 0,
|
||||
"space-before-function-paren": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"no-param-reassign": [
|
||||
"error",
|
||||
{
|
||||
@@ -55,6 +59,9 @@ module.exports = {
|
||||
1,
|
||||
140,
|
||||
2,
|
||||
{
|
||||
ignoreComments: true,
|
||||
}
|
||||
],
|
||||
'import/extensions': [
|
||||
'error',
|
||||
|
||||
58
ADVANCED.md
Normal file
58
ADVANCED.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Orion Verbose configuration
|
||||
|
||||
```ts
|
||||
const orion = new Orion({
|
||||
analyticsAPI: "https://analytics-api.orionprotocol.io",
|
||||
referralAPI: "https://referral-api.orionprotocol.io",
|
||||
networks: {
|
||||
1: {
|
||||
chainId: SupportedChainId.MAINNET,
|
||||
nodeJsonRpc: "https://cloudflare-eth.com/",
|
||||
services: {
|
||||
orionBlockchain: {
|
||||
http: "http://localhost:3000",
|
||||
},
|
||||
orionAggregator: {
|
||||
http: "http://localhost:3001/backend",
|
||||
ws: "http://localhost:3001/v1",
|
||||
},
|
||||
priceFeed: {
|
||||
api: "http://localhost:3002/price-feed",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Also you can set some config as default and override it for some params
|
||||
const orion = new Orion("testing", {
|
||||
analyticsAPI: "https://asdasd.orionprotocol.io",
|
||||
networks: {
|
||||
[SupportedChainId.BSC_TESTNET]: {
|
||||
nodeJsonRpc: "https://data-seed-prebsc-1-s1.binance.org:8545/",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Orion unit init
|
||||
const orionUnit = orion.getUnit("bsc");
|
||||
// OR
|
||||
const orionUnit = orion.getUnit(SupportedChainId.BSC);
|
||||
// OR
|
||||
const orionUnit = new OrionUnit({
|
||||
chainId: SupportedChainId.BSC,
|
||||
nodeJsonRpc: "https://bsc-dataseed.binance.org/",
|
||||
services: {
|
||||
orionBlockchain: {
|
||||
http: "https://orion-bsc-api.orionprotocol.io",
|
||||
},
|
||||
orionAggregator: {
|
||||
http: "https://orion-bsc-api.orionprotocol.io/backend",
|
||||
ws: "https://orion-bsc-api.orionprotocol.io/v1",
|
||||
},
|
||||
priceFeed: {
|
||||
api: "https://orion-bsc-api.orionprotocol.io/price-feed",
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -54,10 +54,11 @@ npm i @orionprotocol/sdk
|
||||
|
||||
```js
|
||||
// Node.js
|
||||
import { OrionUnit } from "@orionprotocol/sdk";
|
||||
import { OrionUnit, Orion } from "@orionprotocol/sdk";
|
||||
import { Wallet } from "ethers";
|
||||
|
||||
const orionUnit = new OrionUnit("bsc", "production"); // eth, bsc, ftm, polygon, okc available
|
||||
const orion = new Orion();
|
||||
const orionUnit = orion.getUnit("bsc"); // eth, bsc, ftm, polygon, okc available
|
||||
const wallet = new Wallet("0x...", orionUnit.provider);
|
||||
// OrionUnit is chain-in-environment abstraction
|
||||
```
|
||||
@@ -73,7 +74,8 @@ const startApp = async (provider: BaseProvider) => {
|
||||
const web3Provider = new providers.Web3Provider(provider);
|
||||
await web3Provider.ready;
|
||||
const signer = web3Provider.getSigner(); // ready to go
|
||||
const orionUnit = new OrionUnit("eth", "production"); // ready to go
|
||||
const orion = new Orion();
|
||||
const orionUnit = orion.getUnit("eth"); // ready to go
|
||||
};
|
||||
|
||||
detectEthereumProvider().then((provider) => {
|
||||
|
||||
@@ -7,8 +7,10 @@ Let's consider integration of Orion Protocol with your UI.
|
||||
Orion Protocol's SDK operates with OrionUnit — chain-in-environment abstraction. "Ethereum-in-production", "bsc-in-production", "fantom-in-testing", etc.
|
||||
|
||||
```ts
|
||||
import { OrionUnit } from "@orionprotocol/sdk";
|
||||
const orionUnit = new OrionUnit("bsc", "production"); // eth, bsc, ftm available
|
||||
import { Orion } from "@orionprotocol/sdk";
|
||||
const orion = new Orion();
|
||||
const bscOrionUnit = orion.getUnit("bsc"); // eth, bsc, ftm, polygon, okc available
|
||||
const ethOrionUnit = orion.getUnit("eth");
|
||||
```
|
||||
|
||||
## 2. Signer accessing
|
||||
|
||||
12
jest.config.ts
Normal file
12
jest.config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { JestConfigWithTsJest } from 'ts-jest';
|
||||
|
||||
const config: JestConfigWithTsJest = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/__tests__/**/*.test.ts'],
|
||||
modulePathIgnorePatterns: ['lib', 'node_modules'],
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest',
|
||||
},
|
||||
};
|
||||
export default config;
|
||||
5750
package-lock.json
generated
5750
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
63
package.json
63
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@orionprotocol/sdk",
|
||||
"version": "0.16.8",
|
||||
"version": "0.17.0-rc.8",
|
||||
"description": "Orion Protocol SDK",
|
||||
"main": "./lib/esm/index.js",
|
||||
"module": "./lib/esm/index.js",
|
||||
@@ -14,12 +14,14 @@
|
||||
"prepare": "npm run build",
|
||||
"prebuild": "tsc",
|
||||
"build": "webpack",
|
||||
"test": "exit 0",
|
||||
"coverage": "jest --coverage",
|
||||
"lint:eslint": "eslint ./src --ext .ts,.js,.tsx,.jsx",
|
||||
"lint:eslint:fix": "eslint ./src --ext .ts,.js,.tsx,.jsx --fix",
|
||||
"postpublish": "npm run publish-npm",
|
||||
"publish-npm": "npm publish --access public --ignore-scripts --@orionprotocol:registry='https://registry.npmjs.org'"
|
||||
"publish-npm": "npm publish --access public --ignore-scripts --@orionprotocol:registry='https://registry.npmjs.org'",
|
||||
"test": "jest",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -37,43 +39,56 @@
|
||||
"url": "https://github.com/orionprotocol/sdk/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.9",
|
||||
"@tsconfig/strictest": "^1.0.2",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/node": "^18.13.0",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/socket.io-client": "1.4.33",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.16.0",
|
||||
"@typescript-eslint/parser": "^5.16.0",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@types/ws": "^8.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||
"@typescript-eslint/parser": "^5.51.0",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^8.2.5",
|
||||
"concurrently": "^7.0.0",
|
||||
"eslint": "^8.12.0",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-config-standard-with-typescript": "^34.0.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-n": "^15.6.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"husky": "^7.0.4",
|
||||
"jest": "^27.5.1",
|
||||
"ts-loader": "^9.3.0",
|
||||
"typescript": "^4.9.3",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
"jest": "^29.4.2",
|
||||
"ts-jest": "^29.0.5",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^4.9.5",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ethersproject/abstract-signer": "^5.6.0",
|
||||
"@ethersproject/providers": "^5.6.2",
|
||||
"@ethersproject/abstract-signer": "^5.7.0",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
"@lukeed/csprng": "^1.0.1",
|
||||
"@orionprotocol/contracts": "0.0.10",
|
||||
"bignumber.js": "^9.0.2",
|
||||
"@orionprotocol/contracts": "0.2.2",
|
||||
"bignumber.js": "^9.1.1",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"ethers": "^5.6.2",
|
||||
"express": "^4.18.2",
|
||||
"isomorphic-unfetch": "^3.1.0",
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
"just-clone": "^5.0.1",
|
||||
"just-clone": "^6.2.0",
|
||||
"merge-anything": "^5.1.4",
|
||||
"neverthrow": "^4.3.1",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^8.5.0",
|
||||
"zod": "^3.17.3"
|
||||
"tiny-invariant": "^1.3.1",
|
||||
"ts-is-present": "^1.2.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^8.12.0",
|
||||
"zod": "^3.20.2"
|
||||
},
|
||||
"homepage": "https://github.com/orionprotocol/sdk#readme",
|
||||
"files": [
|
||||
|
||||
@@ -4,7 +4,7 @@ import clone from 'just-clone';
|
||||
import { ERC20__factory } from '@orionprotocol/contracts';
|
||||
import { utils } from '.';
|
||||
import { APPROVE_ERC20_GAS_LIMIT, NATIVE_CURRENCY_PRECISION } from './constants';
|
||||
import {
|
||||
import type {
|
||||
AggregatedBalanceRequirement, ApproveFix, Asset, BalanceIssue, BalanceRequirement, Source,
|
||||
} from './types';
|
||||
import { denormalizeNumber } from './utils';
|
||||
@@ -82,9 +82,9 @@ export default class BalanceGuard {
|
||||
return requirements
|
||||
.reduce<AggregatedBalanceRequirement[]>((prev, curr) => {
|
||||
const aggregatedBalanceRequirement = prev.find(
|
||||
(item) => item.asset.address === curr.asset.address
|
||||
&& arrayEquals(item.sources, curr.sources)
|
||||
&& item.spenderAddress === curr.spenderAddress,
|
||||
(item) => item.asset.address === curr.asset.address &&
|
||||
arrayEquals(item.sources, curr.sources) &&
|
||||
item.spenderAddress === curr.spenderAddress,
|
||||
);
|
||||
|
||||
if (aggregatedBalanceRequirement) {
|
||||
@@ -163,9 +163,9 @@ export default class BalanceGuard {
|
||||
|
||||
async check(fixAutofixable?: boolean) {
|
||||
this.logger?.(`Balance requirements: ${this.requirements
|
||||
.map((requirement) => `${requirement.amount} ${requirement.asset.name} `
|
||||
+ `for '${requirement.reason}' `
|
||||
+ `from [${requirement.sources.join(' + ')}]`)
|
||||
.map((requirement) => `${requirement.amount} ${requirement.asset.name} ` +
|
||||
`for '${requirement.reason}' ` +
|
||||
`from [${requirement.sources.join(' + ')}]`)
|
||||
.join(', ')}`);
|
||||
|
||||
const remainingBalances = clone(this.balances);
|
||||
@@ -206,9 +206,9 @@ export default class BalanceGuard {
|
||||
balanceIssues.push({
|
||||
asset,
|
||||
sources: ['exchange'],
|
||||
message: `Not enough ${asset.name} on exchange balance. `
|
||||
+ `Needed: ${itemsAmountSum.toString()}, available: ${exchangeBalance?.toString()}. `
|
||||
+ `You need to deposit at least ${lackAmount.toString()} ${asset.name} into exchange contract`,
|
||||
message: `Not enough ${asset.name} on exchange balance. ` +
|
||||
`Needed: ${itemsAmountSum.toString()}, available: ${(exchangeBalance ?? '[UNDEFINED]')?.toString()}. ` +
|
||||
`You need to deposit at least ${lackAmount.toString()} ${asset.name} into exchange contract`,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -260,10 +260,10 @@ export default class BalanceGuard {
|
||||
const exchangeBalance = this.balances?.[asset.name]?.exchange;
|
||||
const available = exchangeBalance?.plus(approvedWalletBalance);
|
||||
|
||||
const issueMessage = `Not enough ${asset.name} on exchange + wallet balance. `
|
||||
+ `Needed: ${itemsAmountSum.toString()}, available: ${available?.toString()} `
|
||||
+ `(exchange: ${exchangeBalance?.toString()}, available (approved): ${approvedWalletBalance.toString()}).`
|
||||
+ ` ${approveIsHelpful
|
||||
const issueMessage = `Not enough ${asset.name} on exchange + wallet balance. ` +
|
||||
`Needed: ${itemsAmountSum.toString()}, available: ${(available ?? '[UNDEFINED]')?.toString()} ` +
|
||||
`(exchange: ${(exchangeBalance ?? '[UNKNOWN]')?.toString()}, available (approved): ${approvedWalletBalance.toString()}).` +
|
||||
` ${approveIsHelpful
|
||||
? `You need approve at least ${lackAmount.toString()} ${asset.name}`
|
||||
: 'Approve is not helpful'}`;
|
||||
if (approveIsHelpful) {
|
||||
@@ -290,11 +290,13 @@ export default class BalanceGuard {
|
||||
asset,
|
||||
sources: ['exchange', 'wallet'],
|
||||
fixes: [
|
||||
...resetRequired ? [{
|
||||
type: 'byApprove' as const,
|
||||
targetAmount: 0,
|
||||
spenderAddress,
|
||||
}] : [],
|
||||
...resetRequired
|
||||
? [{
|
||||
type: 'byApprove' as const,
|
||||
targetAmount: 0,
|
||||
spenderAddress,
|
||||
}]
|
||||
: [],
|
||||
{
|
||||
type: 'byApprove',
|
||||
targetAmount: targetApprove,
|
||||
@@ -351,8 +353,8 @@ export default class BalanceGuard {
|
||||
const approveIsHelpful = approveAvailable.gte(lackAmount);
|
||||
const targetApprove = approvedWalletBalance.plus(lackAmount);
|
||||
|
||||
const issueMessage = `Not enough ${asset.name} on wallet balance. `
|
||||
+ `Needed: ${itemsAmountSum.toString()}, available (approved): ${approvedWalletBalance.toString()}. ${approveIsHelpful
|
||||
const issueMessage = `Not enough ${asset.name} on wallet balance. ` +
|
||||
`Needed: ${itemsAmountSum.toString()}, available (approved): ${approvedWalletBalance.toString()}. ${approveIsHelpful
|
||||
? `You need approve at least ${lackAmount.toString()} ${asset.name}`
|
||||
: 'Approve is not helpful'}`;
|
||||
if (approveIsHelpful) {
|
||||
@@ -379,11 +381,13 @@ export default class BalanceGuard {
|
||||
asset,
|
||||
sources: ['wallet'],
|
||||
fixes: [
|
||||
...resetRequired ? [{
|
||||
type: 'byApprove' as const,
|
||||
targetAmount: 0,
|
||||
spenderAddress,
|
||||
}] : [],
|
||||
...resetRequired
|
||||
? [{
|
||||
type: 'byApprove' as const,
|
||||
targetAmount: 0,
|
||||
spenderAddress,
|
||||
}]
|
||||
: [],
|
||||
{
|
||||
type: 'byApprove',
|
||||
targetAmount: targetApprove,
|
||||
@@ -418,8 +422,8 @@ export default class BalanceGuard {
|
||||
balanceIssues.push({
|
||||
asset,
|
||||
sources: ['wallet'],
|
||||
message: `Not enough ${asset.name} on wallet balance. `
|
||||
+ `You need to deposit at least ${lackAmount.toString()} ${asset.name} into wallet contract`,
|
||||
message: `Not enough ${asset.name} on wallet balance. ` +
|
||||
`You need to deposit at least ${lackAmount.toString()} ${asset.name} into wallet contract`,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -428,7 +432,10 @@ export default class BalanceGuard {
|
||||
const unfixed = await this.fixAllAutofixableBalanceIssues(balanceIssues);
|
||||
if (unfixed.length > 0) throw new Error(`Balance issues: ${unfixed.map((issue, i) => `${i + 1}. ${issue.message}`).join('\n')}`);
|
||||
} else if (balanceIssues.length > 0) {
|
||||
throw new Error(`Balance issues (address ${walletAddress}): ${balanceIssues.map((issue, i) => `${i + 1}. ${issue.message}`).join('\n')}`);
|
||||
throw new Error(
|
||||
`Balance issues (address ${walletAddress}): ` +
|
||||
`${balanceIssues.map((issue, i) => `${i + 1}. ${issue.message}`).join('\n')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
143
src/Orion/index.ts
Normal file
143
src/Orion/index.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { merge } from 'merge-anything';
|
||||
import { chains, envs } from '../config';
|
||||
import OrionUnit from '../OrionUnit';
|
||||
import OrionAnalytics from '../services/OrionAnalytics';
|
||||
import { ReferralSystem } from '../services/ReferralSystem';
|
||||
import { DeepPartial, SupportedChainId, VerboseOrionUnitConfig } from '../types';
|
||||
import { isValidChainId } from '../utils';
|
||||
|
||||
type EnvConfig = {
|
||||
analyticsAPI: string;
|
||||
referralAPI: string;
|
||||
networks: Partial<
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseOrionUnitConfig
|
||||
>
|
||||
>;
|
||||
}
|
||||
|
||||
// type KnownEnv = 'testing' | 'staging' | 'production';
|
||||
|
||||
export default class Orion {
|
||||
public readonly env?: string;
|
||||
|
||||
public readonly units: Partial<Record<SupportedChainId, OrionUnit>>;
|
||||
|
||||
public readonly orionAnalytics: OrionAnalytics;
|
||||
|
||||
public readonly referralSystem: ReferralSystem;
|
||||
|
||||
constructor();
|
||||
constructor(
|
||||
env: string,
|
||||
overrides?: DeepPartial<EnvConfig>
|
||||
);
|
||||
|
||||
constructor(config: EnvConfig);
|
||||
|
||||
// TODO: get tradable assets (aggregated)
|
||||
|
||||
// TODO: get tradable pairs (aggregated)
|
||||
|
||||
// TODO: bridge
|
||||
|
||||
constructor(
|
||||
envOrConfig: string | EnvConfig = 'production',
|
||||
overrides?: DeepPartial<EnvConfig>
|
||||
) {
|
||||
let config: EnvConfig;
|
||||
if (typeof envOrConfig === 'string') {
|
||||
const envConfig = envs[envOrConfig];
|
||||
if (!envConfig) throw new Error(`Invalid environment: ${envOrConfig}. Available environments: ${Object.keys(envs).join(', ')}`);
|
||||
this.env = envOrConfig;
|
||||
config = {
|
||||
analyticsAPI: envConfig.analyticsAPI,
|
||||
referralAPI: envConfig.referralAPI,
|
||||
networks: Object.entries(envConfig.networks).map(([chainId, networkConfig]) => {
|
||||
if (!isValidChainId(chainId)) throw new Error(`Invalid chainId: ${chainId}`);
|
||||
const chainConfig = chains[chainId];
|
||||
if (!chainConfig) throw new Error(`Chain config not found: ${chainId}. Available chains: ${Object.keys(chains).join(', ')}`);
|
||||
|
||||
return {
|
||||
env: envOrConfig,
|
||||
chainId,
|
||||
api: networkConfig.api,
|
||||
nodeJsonRpc: chainConfig.rpc,
|
||||
services: {
|
||||
orionBlockchain: {
|
||||
http: networkConfig.api + networkConfig.services.blockchain.http,
|
||||
},
|
||||
orionAggregator: {
|
||||
http: networkConfig.api + networkConfig.services.aggregator.http,
|
||||
ws: networkConfig.api + networkConfig.services.aggregator.ws,
|
||||
},
|
||||
priceFeed: {
|
||||
api: networkConfig.api + networkConfig.services.priceFeed.all,
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.reduce<Partial<Record<SupportedChainId, VerboseOrionUnitConfig>>>((acc, cur) => {
|
||||
acc[cur.chainId] = cur;
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
if (overrides) {
|
||||
config = merge(config, overrides);
|
||||
}
|
||||
} else {
|
||||
config = envOrConfig;
|
||||
}
|
||||
|
||||
this.orionAnalytics = new OrionAnalytics(config.analyticsAPI);
|
||||
this.referralSystem = new ReferralSystem(config.referralAPI);
|
||||
|
||||
this.units = Object.entries(config.networks)
|
||||
.reduce<Partial<Record<SupportedChainId, OrionUnit>>>((acc, [chainId, networkConfig]) => {
|
||||
if (!isValidChainId(chainId)) throw new Error(`Invalid chainId: ${chainId}`);
|
||||
const chainConfig = chains[chainId];
|
||||
if (!chainConfig) throw new Error(`Invalid chainId: ${chainId}`);
|
||||
|
||||
const orionUnit = new OrionUnit({
|
||||
// env: networkConfig.env,
|
||||
chainId,
|
||||
// api: networkConfig.api,
|
||||
nodeJsonRpc: networkConfig.nodeJsonRpc,
|
||||
services: networkConfig.services,
|
||||
});
|
||||
return {
|
||||
...acc,
|
||||
[chainId]: orionUnit,
|
||||
}
|
||||
}, {});
|
||||
}
|
||||
|
||||
get unitsArray() {
|
||||
return Object.entries(this.units).map(([, unit]) => unit);
|
||||
}
|
||||
|
||||
getUnit(chainId: SupportedChainId): OrionUnit;
|
||||
|
||||
getUnit(networkCode: string): OrionUnit;
|
||||
|
||||
getUnit(networkCodeOrChainId: string): OrionUnit {
|
||||
let unit: OrionUnit | undefined;
|
||||
if (isValidChainId(networkCodeOrChainId)) {
|
||||
unit = this.units[networkCodeOrChainId];
|
||||
} else {
|
||||
unit = this.unitsArray.find((u) => u.networkCode === networkCodeOrChainId);
|
||||
}
|
||||
if (!unit) {
|
||||
throw new Error(
|
||||
`Invalid network code: ${networkCodeOrChainId}. ` +
|
||||
`Available network codes: ${this.unitsArray.map((u) => u.networkCode).join(', ')}`);
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
getSiblingsOf(chainId: SupportedChainId) {
|
||||
return this.unitsArray.filter((unit) => unit.chainId !== chainId);
|
||||
}
|
||||
}
|
||||
@@ -85,11 +85,11 @@ export default async function getSwapInfo({
|
||||
if (options?.poolOnly) {
|
||||
route = 'pool';
|
||||
} else if (
|
||||
swapExchanges !== undefined
|
||||
&& poolExchangesList.length > 0
|
||||
&& swapExchanges.length === 1
|
||||
&& firstSwapExchange
|
||||
&& poolExchangesList.some((poolExchange) => poolExchange === firstSwapExchange)
|
||||
swapExchanges !== undefined &&
|
||||
poolExchangesList.length > 0 &&
|
||||
swapExchanges.length === 1 &&
|
||||
firstSwapExchange &&
|
||||
poolExchangesList.some((poolExchange) => poolExchange === firstSwapExchange)
|
||||
) {
|
||||
route = 'pool';
|
||||
} else {
|
||||
|
||||
@@ -170,11 +170,11 @@ export default async function swapMarket({
|
||||
options?.logger?.('Swap is through pool (because "poolOnly" option is true)');
|
||||
route = 'pool';
|
||||
} else if (
|
||||
swapExchanges !== undefined
|
||||
&& poolExchangesList.length > 0
|
||||
&& swapExchanges.length === 1
|
||||
&& firstSwapExchange
|
||||
&& poolExchangesList.some((poolExchange) => poolExchange === firstSwapExchange)
|
||||
swapExchanges !== undefined &&
|
||||
poolExchangesList.length > 0 &&
|
||||
swapExchanges.length === 1 &&
|
||||
firstSwapExchange &&
|
||||
poolExchangesList.some((poolExchange) => poolExchange === firstSwapExchange)
|
||||
) {
|
||||
options?.logger?.(`Swap is through pool [via ${firstSwapExchange}] (detected by "exchanges" field)`);
|
||||
route = 'pool';
|
||||
|
||||
@@ -1,34 +1,21 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { OrionAggregator } from '../services/OrionAggregator';
|
||||
import OrionAnalytics from '../services/OrionAnalytics';
|
||||
import { OrionBlockchain } from '../services/OrionBlockchain';
|
||||
import { PriceFeed } from '../services/PriceFeed';
|
||||
import { SupportedChainId } from '../types';
|
||||
import type { SupportedChainId, VerboseOrionUnitConfig } from '../types';
|
||||
import Exchange from './Exchange';
|
||||
import FarmingManager from './FarmingManager';
|
||||
import { chains, envs } from '../config';
|
||||
import { isValidChainId } from '../utils';
|
||||
import { ReferralSystem } from '../services/ReferralSystem';
|
||||
import { chains } from '../config';
|
||||
|
||||
const orionAnalyticsUrl = 'https://trade.orionprotocol.io';
|
||||
// type KnownConfig = {
|
||||
// env: string;
|
||||
// chainId: SupportedChainId;
|
||||
// }
|
||||
|
||||
// type OrionUnitConfig = KnownConfig | VerboseOrionUnitConfig;
|
||||
|
||||
type Options = {
|
||||
api?: string;
|
||||
nodeJsonRpc?: string;
|
||||
services?: {
|
||||
orionBlockchain?: {
|
||||
api?: string;
|
||||
},
|
||||
orionAggregator?: {
|
||||
api?: string;
|
||||
},
|
||||
priceFeed?: {
|
||||
api?: string;
|
||||
},
|
||||
}
|
||||
};
|
||||
export default class OrionUnit {
|
||||
public readonly env: string;
|
||||
// public readonly env?: string;
|
||||
|
||||
public readonly networkCode: string;
|
||||
|
||||
@@ -42,122 +29,69 @@ export default class OrionUnit {
|
||||
|
||||
public readonly priceFeed: PriceFeed;
|
||||
|
||||
public readonly orionAnalytics: OrionAnalytics;
|
||||
|
||||
public readonly exchange: Exchange;
|
||||
|
||||
public readonly farmingManager: FarmingManager;
|
||||
|
||||
public readonly apiUrl: string;
|
||||
|
||||
public readonly referralSystem: ReferralSystem;
|
||||
|
||||
constructor(
|
||||
chain: string,
|
||||
env: string,
|
||||
options?: Options,
|
||||
) {
|
||||
let chainId: SupportedChainId;
|
||||
let customApi: string | undefined;
|
||||
let customRpc: string | undefined;
|
||||
let chainInfo: typeof chains[SupportedChainId] | undefined;
|
||||
|
||||
if (!(env in envs)) {
|
||||
if (env === 'custom') {
|
||||
if (!options?.api) throw new Error('Your env is custom. You should provide api url in options');
|
||||
const { api } = options;
|
||||
customApi = api;
|
||||
if (isValidChainId(chain)) {
|
||||
chainId = chain;
|
||||
chainInfo = chains[chain];
|
||||
} else throw new Error('Your chainId is invalid');
|
||||
} else {
|
||||
throw new Error(`Env '${env}' not found. Available environments is: ${Object.keys(envs).join(', ')}`);
|
||||
}
|
||||
} else {
|
||||
const envInfo = envs[env];
|
||||
const envNetworks = envInfo?.networks;
|
||||
if (envNetworks === undefined) throw new Error('Env networks is undefined (constructor)');
|
||||
|
||||
if (isValidChainId(chain)) chainId = chain;
|
||||
else {
|
||||
const targetChains = Object
|
||||
.keys(chains)
|
||||
.filter(isValidChainId)
|
||||
.filter((ch) => {
|
||||
const chInfo = chains[ch];
|
||||
if (!chInfo) return false;
|
||||
return (chInfo.chainId in envNetworks)
|
||||
&& (chInfo.code.toLowerCase() === chain.toLowerCase());
|
||||
});
|
||||
if (targetChains.length !== 1) {
|
||||
throw new Error(
|
||||
targetChains.length > 1
|
||||
? 'Ambiguation detected. '
|
||||
+ `Found ${targetChains.length} chain ids [${targetChains.join(', ')}] for chain name '${chain}' in env '${env}'. Expected 1.`
|
||||
: `Chains not found for chain name '${chain}' in env '${env}'.`,
|
||||
);
|
||||
}
|
||||
const firstTargetChain = targetChains[0];
|
||||
if (firstTargetChain === undefined) throw new Error('First target chain is undefined');
|
||||
chainId = firstTargetChain;
|
||||
}
|
||||
|
||||
if (!(chainId in envNetworks)) {
|
||||
throw new Error(`Chain '${chainId}' not found. `
|
||||
+ `Available chains in selected environment (${env}) is: ${Object.keys(envNetworks).join(', ')}`);
|
||||
}
|
||||
|
||||
const envNetworkInfo = envNetworks[chainId];
|
||||
chainInfo = chains[chainId];
|
||||
|
||||
if (!envNetworkInfo) throw new Error('Env network info is required');
|
||||
|
||||
customApi = envNetworkInfo.api;
|
||||
customRpc = envNetworkInfo.rpc;
|
||||
}
|
||||
// constructor(config: KnownConfig);
|
||||
// constructor(config: VerboseConfig);
|
||||
|
||||
constructor(config: VerboseOrionUnitConfig) {
|
||||
const chainInfo = chains[config.chainId];
|
||||
if (!chainInfo) throw new Error('Chain info is required');
|
||||
|
||||
this.chainId = chainId;
|
||||
// if ('env' in config)
|
||||
// this.env = config.env;
|
||||
this.chainId = config.chainId;
|
||||
this.networkCode = chainInfo.code;
|
||||
this.provider = new ethers.providers.StaticJsonRpcProvider(options?.nodeJsonRpc ?? customRpc ?? chainInfo.rpc);
|
||||
this.env = env;
|
||||
this.apiUrl = customApi;
|
||||
this.provider = new ethers.providers.StaticJsonRpcProvider(config.nodeJsonRpc);
|
||||
|
||||
this.orionBlockchain = new OrionBlockchain(
|
||||
options?.services?.orionBlockchain?.api
|
||||
?? options?.api
|
||||
?? customApi,
|
||||
);
|
||||
|
||||
const oaUrl = new URL(options?.services?.orionAggregator?.api ?? options?.api ?? customApi);
|
||||
const oaWsProtocol = oaUrl.protocol === 'https:' ? 'wss' : 'ws';
|
||||
const orionAggregatorWsUrl = `${oaWsProtocol}://${oaUrl.host + (oaUrl.pathname === '/' ? '' : oaUrl.pathname)}/v1`;
|
||||
this.orionBlockchain = new OrionBlockchain(config.services.orionBlockchain.http);
|
||||
this.orionAggregator = new OrionAggregator(
|
||||
options?.services?.orionAggregator?.api ?? `${options?.api ?? customApi}/backend`,
|
||||
orionAggregatorWsUrl,
|
||||
config.services.orionAggregator.http,
|
||||
config.services.orionAggregator.ws,
|
||||
);
|
||||
this.priceFeed = new PriceFeed(
|
||||
options?.services?.priceFeed?.api
|
||||
?? `${options?.api ?? customApi}/price-feed`,
|
||||
);
|
||||
this.orionAnalytics = new OrionAnalytics(orionAnalyticsUrl);
|
||||
this.priceFeed = new PriceFeed(config.services.priceFeed.api);
|
||||
this.exchange = new Exchange(this);
|
||||
this.farmingManager = new FarmingManager(this);
|
||||
this.referralSystem = new ReferralSystem(options?.api ?? customApi, env);
|
||||
}
|
||||
|
||||
get siblings() {
|
||||
const envInfo = envs[this.env];
|
||||
const envNetworks = envInfo?.networks;
|
||||
// get siblings() {
|
||||
// if (!this.env) throw new Error('Sibling is not available, because env is not set');
|
||||
|
||||
if (envNetworks === undefined) throw new Error('Env networks is undefined (siblings)');
|
||||
// const envInfo = envs[this.env];
|
||||
// const envNetworks = envInfo?.networks;
|
||||
|
||||
const siblingsNetworks = Object
|
||||
.keys(envNetworks)
|
||||
.filter(isValidChainId)
|
||||
.filter((chainId) => chainId !== this.chainId);
|
||||
return siblingsNetworks.map((chainId) => new OrionUnit(chainId, this.env));
|
||||
}
|
||||
// if (envNetworks === undefined) throw new Error('Env networks is undefined (siblings)');
|
||||
|
||||
// const orionUnits: OrionUnit[] = [];
|
||||
// Object
|
||||
// .entries(envNetworks)
|
||||
// .forEach(([chainId, config]) => {
|
||||
// if (!isValidChainId(chainId)) throw new Error('Invalid chainId');
|
||||
// if (chainId !== this.chainId) {
|
||||
// const chainConfig = chains[chainId];
|
||||
// if (!chainConfig) throw new Error('Chain config is required');
|
||||
// const orionUnit = new OrionUnit({
|
||||
// api: config.api,
|
||||
// chainId,
|
||||
// nodeJsonRpc: chainConfig.rpc ?? config.rpc,
|
||||
// services: {
|
||||
// orionBlockchain: {
|
||||
// http: config.api + config.services.blockchain.http,
|
||||
// },
|
||||
// orionAggregator: {
|
||||
// http: config.api + config.services.aggregator.http,
|
||||
// ws: config.api + config.services.aggregator.ws,
|
||||
// },
|
||||
// priceFeed: {
|
||||
// api: config.api + config.services.priceFeed.all,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// orionUnits.push(orionUnit);
|
||||
// }
|
||||
// });
|
||||
// return orionUnits;
|
||||
// }
|
||||
}
|
||||
|
||||
319
src/__tests__/basic.test.ts
Normal file
319
src/__tests__/basic.test.ts
Normal file
@@ -0,0 +1,319 @@
|
||||
// import { ethers } from 'ethers';
|
||||
import Orion from '../Orion';
|
||||
import OrionAnalytics from '../services/OrionAnalytics';
|
||||
import { ReferralSystem } from '../services/ReferralSystem';
|
||||
import simpleFetch from '../simpleFetch';
|
||||
import { SupportedChainId } from '../types';
|
||||
import express from 'express';
|
||||
import WebSocket from 'ws';
|
||||
import http from 'http';
|
||||
import httpToWS from '../utils/httpToWS';
|
||||
|
||||
const createServer = (externalHost: string) => {
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
const wss = new WebSocket.Server({ server });
|
||||
|
||||
let externalWs: WebSocket | null = null;
|
||||
wss.on('connection', (ws, req) => {
|
||||
const targetUrl = httpToWS(`${externalHost}${req.url}`);
|
||||
externalWs = new WebSocket(targetUrl);
|
||||
|
||||
externalWs.on('open', () => {
|
||||
ws.on('message', message => {
|
||||
externalWs?.send(message);
|
||||
});
|
||||
|
||||
externalWs?.on('message', message => {
|
||||
ws.send(message);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get(
|
||||
'*',
|
||||
async (req, res) => {
|
||||
const routeFromURL = req.url;
|
||||
try {
|
||||
const targetUrl = `${externalHost}${routeFromURL}`;
|
||||
const response = await fetch(targetUrl);
|
||||
const text = await response.text();
|
||||
res.send(text);
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
error: 'Failed to retrieve data from external resource'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(0);
|
||||
const address = server.address();
|
||||
|
||||
if (typeof address === 'string') {
|
||||
throw new Error(`Server address is a string: ${address}`);
|
||||
}
|
||||
|
||||
return {
|
||||
port: address?.port,
|
||||
close: () => new Promise((resolve) => {
|
||||
externalWs?.close();
|
||||
server.close(resolve);
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
describe('Orion', () => {
|
||||
test('Init Orion testing', () => {
|
||||
const orion = new Orion('testing');
|
||||
expect(orion.orionAnalytics).toBeInstanceOf(OrionAnalytics);
|
||||
expect(orion.referralSystem).toBeInstanceOf(ReferralSystem);
|
||||
expect(orion.unitsArray.length).toBe(4); // eth, bsc, polygon, fantom
|
||||
|
||||
const orionUnitBSC = orion.units[SupportedChainId.BSC_TESTNET];
|
||||
expect(orionUnitBSC?.chainId).toBe(SupportedChainId.BSC_TESTNET);
|
||||
// expect(orionUnitBSC?.env).toBe('testing');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.BSC_TESTNET)).toHaveLength(3);
|
||||
expect(orionUnitBSC?.networkCode).toBe('bsc');
|
||||
|
||||
const orionUnitRopsten = orion.units[SupportedChainId.ROPSTEN]
|
||||
expect(orionUnitRopsten?.chainId).toBe(SupportedChainId.ROPSTEN);
|
||||
// expect(orionUnitRopsten?.env).toBe('testing');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.ROPSTEN)).toHaveLength(3);
|
||||
expect(orionUnitRopsten?.networkCode).toBe('eth');
|
||||
|
||||
const orionUnitPolygon = orion.units[SupportedChainId.POLYGON_TESTNET];
|
||||
expect(orionUnitPolygon?.chainId).toBe(SupportedChainId.POLYGON_TESTNET);
|
||||
// expect(orionUnitPolygon?.env).toBe('testing');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.POLYGON_TESTNET)).toHaveLength(3);
|
||||
expect(orionUnitPolygon?.networkCode).toBe('polygon');
|
||||
|
||||
const orionUnitFantom = orion.units[SupportedChainId.FANTOM_TESTNET];
|
||||
expect(orionUnitFantom?.chainId).toBe(SupportedChainId.FANTOM_TESTNET);
|
||||
// expect(orionUnitFantom?.env).toBe('testing');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.FANTOM_TESTNET)).toHaveLength(3);
|
||||
expect(orionUnitFantom?.networkCode).toBe('ftm');
|
||||
});
|
||||
|
||||
test('Init Orion production', () => {
|
||||
const orion = new Orion();
|
||||
expect(orion.env).toBe('production');
|
||||
expect(orion.orionAnalytics).toBeInstanceOf(OrionAnalytics);
|
||||
expect(orion.referralSystem).toBeInstanceOf(ReferralSystem);
|
||||
expect(orion.unitsArray.length).toBe(5); // eth, bsc, polygon, fantom, okc
|
||||
|
||||
const orionUnitBSC = orion.units[SupportedChainId.BSC];
|
||||
expect(orionUnitBSC?.chainId).toBe(SupportedChainId.BSC);
|
||||
// expect(orionUnitBSC?.env).toBe('production');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.BSC)).toHaveLength(4);
|
||||
expect(orionUnitBSC?.networkCode).toBe('bsc');
|
||||
|
||||
const orionUnitETH = orion.units[SupportedChainId.MAINNET]
|
||||
expect(orionUnitETH?.chainId).toBe(SupportedChainId.MAINNET);
|
||||
// expect(orionUnitETH?.env).toBe('production');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.MAINNET)).toHaveLength(4);
|
||||
expect(orionUnitETH?.networkCode).toBe('eth');
|
||||
|
||||
const orionUnitPolygon = orion.units[SupportedChainId.POLYGON];
|
||||
expect(orionUnitPolygon?.chainId).toBe(SupportedChainId.POLYGON);
|
||||
// expect(orionUnitPolygon?.env).toBe('production');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.POLYGON)).toHaveLength(4);
|
||||
expect(orionUnitPolygon?.networkCode).toBe('polygon');
|
||||
|
||||
const orionUnitFantom = orion.units[SupportedChainId.FANTOM_OPERA];
|
||||
expect(orionUnitFantom?.chainId).toBe(SupportedChainId.FANTOM_OPERA);
|
||||
// expect(orionUnitFantom?.env).toBe('production');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.FANTOM_OPERA)).toHaveLength(4);
|
||||
expect(orionUnitFantom?.networkCode).toBe('ftm');
|
||||
|
||||
const orionUnitOKC = orion.units[SupportedChainId.OKC];
|
||||
expect(orionUnitOKC?.chainId).toBe(SupportedChainId.OKC);
|
||||
// expect(orionUnitOKC?.env).toBe('production');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.OKC)).toHaveLength(4);
|
||||
expect(orionUnitOKC?.networkCode).toBe('okc');
|
||||
});
|
||||
|
||||
test('Init Orion custom', async () => {
|
||||
const server0 = createServer('https://trade.orionprotocol.io');
|
||||
const server1 = createServer('https://trade.orionprotocol.io');
|
||||
const server2 = createServer('https://trade.orionprotocol.io');
|
||||
|
||||
const orionBlockchainAPI = `http://localhost:${server0.port}`;
|
||||
const orionAggregatorAPI = `http://localhost:${server1.port}`;
|
||||
const orionPriceFeedAPI = `http://localhost:${server2.port}`;
|
||||
|
||||
const orion = new Orion({
|
||||
analyticsAPI: 'https://analytics-api.orionprotocol.io',
|
||||
referralAPI: 'https://referral-api.orionprotocol.io',
|
||||
networks: {
|
||||
1: {
|
||||
// api: 'https://api.orionprotocol.io',
|
||||
chainId: SupportedChainId.MAINNET,
|
||||
nodeJsonRpc: 'https://cloudflare-eth.com/',
|
||||
services: {
|
||||
orionBlockchain: {
|
||||
http: orionBlockchainAPI,
|
||||
},
|
||||
orionAggregator: {
|
||||
http: orionAggregatorAPI + '/backend',
|
||||
ws: `http://localhost:${server1.port}/v1`,
|
||||
},
|
||||
priceFeed: {
|
||||
api: orionPriceFeedAPI + '/price-feed',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const orionUnit = orion.unitsArray[0];
|
||||
if (!orionUnit) {
|
||||
throw new Error('Orion unit is not defined');
|
||||
}
|
||||
expect(orion.orionAnalytics).toBeInstanceOf(OrionAnalytics);
|
||||
expect(orion.referralSystem).toBeInstanceOf(ReferralSystem);
|
||||
expect(orion.unitsArray.length).toBe(1); // eth
|
||||
expect(orion.orionAnalytics.api).toBe('https://analytics-api.orionprotocol.io');
|
||||
expect(orion.referralSystem.api).toBe('https://referral-api.orionprotocol.io');
|
||||
expect(orionUnit.chainId).toBe(SupportedChainId.MAINNET);
|
||||
// expect(orionUnit.env).toBeUndefined();
|
||||
// expect(orion.units[0]?.orionAggregator.api).toBe('http://localhost:3001');
|
||||
expect(orionUnit.orionAggregator.ws.api).toBe(`ws://localhost:${server1.port}/v1`);
|
||||
expect(orionUnit.orionBlockchain.api).toBe(orionBlockchainAPI);
|
||||
expect(orionUnit.priceFeed.api).toBe(orionPriceFeedAPI + '/price-feed');
|
||||
expect(orionUnit.provider.connection.url).toBe('https://cloudflare-eth.com/');
|
||||
|
||||
const info = await simpleFetch(orionUnit.orionBlockchain.getInfo)();
|
||||
expect(info).toBeDefined();
|
||||
|
||||
const spotData = await simpleFetch(orionUnit.orionAggregator.getPairConfigs)('spot');
|
||||
expect(spotData).toBeDefined();
|
||||
|
||||
const priceData = await simpleFetch(orionUnit.priceFeed.getCandles)(
|
||||
'BTC-USDT',
|
||||
Math.floor((Date.now() - 1000 * 60 * 60 * 24 * 30) / 1000), // 1 month ago
|
||||
Math.floor(Date.now() / 1000), // now
|
||||
'1d'
|
||||
);
|
||||
expect(priceData).toBeDefined();
|
||||
|
||||
const allTickersDone = await new Promise<boolean>((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error('Timeout'));
|
||||
}, 10000);
|
||||
|
||||
const { unsubscribe } = orionUnit.priceFeed.ws.subscribe(
|
||||
'allTickers',
|
||||
{
|
||||
callback: () => {
|
||||
resolve(true);
|
||||
unsubscribe();
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
)
|
||||
});
|
||||
expect(allTickersDone).toBe(true);
|
||||
|
||||
await server0.close();
|
||||
await server1.close();
|
||||
await server2.close();
|
||||
});
|
||||
|
||||
test('Init Orion testing with overrides', () => {
|
||||
const orion = new Orion('testing', {
|
||||
analyticsAPI: 'https://asdasd.orionprotocol.io',
|
||||
referralAPI: 'https://zxczxc.orionprotocol.io',
|
||||
networks: {
|
||||
[SupportedChainId.BSC_TESTNET]: {
|
||||
nodeJsonRpc: 'https://data-seed-prebsc-1-s1.binance.org:8545/',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const bscOrionUnit = orion.units[SupportedChainId.BSC_TESTNET]
|
||||
expect(bscOrionUnit?.provider.connection.url).toBe('https://data-seed-prebsc-1-s1.binance.org:8545/');
|
||||
expect(orion.orionAnalytics.api).toBe('https://asdasd.orionprotocol.io');
|
||||
expect(orion.referralSystem.api).toBe('https://zxczxc.orionprotocol.io');
|
||||
});
|
||||
|
||||
test('Orion Responses', async () => {
|
||||
const orion = new Orion('testing');
|
||||
|
||||
const orionUnitBSC = orion.units[SupportedChainId.BSC_TESTNET]
|
||||
if (!orionUnitBSC) {
|
||||
throw new Error('Orion unit not found');
|
||||
}
|
||||
const info = await simpleFetch(orionUnitBSC.orionBlockchain.getInfo)();
|
||||
expect(info).toBeDefined();
|
||||
expect(info.chainId).toBe(97);
|
||||
expect(info.chainName).toBe('bsc-testnet');
|
||||
|
||||
const pairConfigs = await simpleFetch(orionUnitBSC.orionAggregator.getPairConfigs)('spot');
|
||||
expect(pairConfigs).toBeDefined();
|
||||
expect(pairConfigs.length).toBeGreaterThan(0);
|
||||
|
||||
const aobusDone = await new Promise<boolean>((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error('Timeout'));
|
||||
}, 10000);
|
||||
|
||||
orionUnitBSC.orionAggregator.ws.subscribe('aobus', {
|
||||
payload: 'ORN-USDT',
|
||||
callback: () => {
|
||||
resolve(true);
|
||||
orionUnitBSC.orionAggregator.ws.destroy();
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
})
|
||||
});
|
||||
expect(aobusDone).toBe(true);
|
||||
// const candles = await simpleFetch(orionUnitBSC.priceFeed.getCandles)(
|
||||
// 'BTC-USDT',
|
||||
// Math.floor((Date.now() - 1000 * 60 * 60 * 24 * 30) / 1000), // 1 month ago
|
||||
// Math.floor(Date.now() / 1000), // now
|
||||
// '1d'
|
||||
// );
|
||||
// expect(candles).toBeDefined();
|
||||
|
||||
// const allTickersDone = await new Promise<boolean>((resolve, reject) => {
|
||||
// const timeout = setTimeout(() => {
|
||||
// reject(new Error('Timeout'));
|
||||
// }, 10000);
|
||||
|
||||
// const { unsubscribe } = orionUnitBSC.priceFeed.ws.subscribe(
|
||||
// 'allTickers',
|
||||
// {
|
||||
// callback: () => {
|
||||
// resolve(true);
|
||||
// unsubscribe();
|
||||
// clearTimeout(timeout);
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// });
|
||||
// expect(allTickersDone).toBe(true);
|
||||
|
||||
// const blockNumber = await orionUnitBSC.provider.getBlockNumber();
|
||||
// expect(blockNumber).toBeDefined();
|
||||
// const network = await orionUnitBSC.provider.getNetwork();
|
||||
// expect(network.chainId).toBe(97);
|
||||
|
||||
// const stats = await simpleFetch(orion.orionAnalytics.getOverview)();
|
||||
// expect(stats).toBeDefined();
|
||||
|
||||
// const zeroAddressWithout0x = ethers.constants.AddressZero.slice(2);
|
||||
// expect(simpleFetch(orion.referralSystem.getMiniStats)(zeroAddressWithout0x))
|
||||
// .rejects
|
||||
// .toThrow('empty reward history');
|
||||
});
|
||||
|
||||
test('Get Orion unit by networkCode', () => {
|
||||
const orionTesting = new Orion('testing');
|
||||
const orionUnitBSCTesting = orionTesting.getUnit('bsc');
|
||||
expect(orionUnitBSCTesting.chainId).toBe(SupportedChainId.BSC_TESTNET);
|
||||
|
||||
const orionMainnet = new Orion('production');
|
||||
const orionUnitBSCMainnet = orionMainnet.getUnit('bsc');
|
||||
expect(orionUnitBSCMainnet.chainId).toBe(SupportedChainId.BSC);
|
||||
})
|
||||
});
|
||||
@@ -1,68 +1,350 @@
|
||||
{
|
||||
"production": {
|
||||
"analyticsAPI": "https://trade.orionprotocol.io/api/stats",
|
||||
"referralAPI": "https://trade.orionprotocol.io/referral-api",
|
||||
"networks": {
|
||||
"1": {
|
||||
"api": "https://trade.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
},
|
||||
"liquidityMigratorAddress": "0x23a1820a47BcD022E29f6058a5FD224242F50D1A"
|
||||
},
|
||||
"56": {
|
||||
"api": "https://trade-exp.orionprotocol.io"
|
||||
"api": "https://trade-exp.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"250": {
|
||||
"api": "https://trade-ftm.orionprotocol.io"
|
||||
"api": "https://trade-ftm.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"137": {
|
||||
"api": "https://trade-polygon.orionprotocol.io"
|
||||
"api": "https://trade-polygon.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"66": {
|
||||
"api": "https://trade-okc.orionprotocol.io"
|
||||
"api": "https://trade-okc.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"testing": {
|
||||
"analyticsAPI": "https://trade.orionprotocol.io/api/stats",
|
||||
"referralAPI": "https://testing.orionprotocol.io/referral-api",
|
||||
"networks": {
|
||||
"97": {
|
||||
"api": "https://testing.orionprotocol.io/bsc-testnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
},
|
||||
"liquidityMigratorAddress": "0x01b10dds12478C88A5E18e2707E729906bC25CfF6"
|
||||
},
|
||||
"3": {
|
||||
"api": "https://testing.orionprotocol.io/eth-ropsten",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
},
|
||||
"liquidityMigratorAddress": "0x36969a25622AE31bA9946e0c8151f0dc08b3A1c8"
|
||||
},
|
||||
"4002": {
|
||||
"api": "https://testing.orionprotocol.io/ftm-testnet"
|
||||
"api": "https://testing.orionprotocol.io/ftm-testnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"80001": {
|
||||
"api": "https://testing.orionprotocol.io/polygon-mumbai"
|
||||
"api": "https://testing.orionprotocol.io/polygon-mumbai",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"staging": {
|
||||
"analyticsAPI": "https://trade.orionprotocol.io/api/stats",
|
||||
"referralAPI": "https://staging.orionprotocol.io/referral-api",
|
||||
"networks": {
|
||||
"1": {
|
||||
"api": "https://staging.orionprotocol.io"
|
||||
"api": "https://staging.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"56": {
|
||||
"api": "https://staging-bsc.orionprotocol.io"
|
||||
"api": "https://staging-bsc.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"250": {
|
||||
"api": "https://staging-ftm.orionprotocol.io"
|
||||
"api": "https://staging-ftm.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"137": {
|
||||
"api": "https://staging-polygon.orionprotocol.io"
|
||||
"api": "https://staging-polygon.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"66": {
|
||||
"api": "https://staging-okc.orionprotocol.io"
|
||||
"api": "https://staging-okc.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"experimental": {
|
||||
"analyticsAPI": "https://trade.orionprotocol.io/api/stats",
|
||||
"referralAPI": "https://testing.orionprotocol.io/referral-api",
|
||||
"networks": {
|
||||
"97": {
|
||||
"api": "https://dn-dev.orionprotocol.io/bsc-testnet"
|
||||
"api": "https://dn-dev.orionprotocol.io/bsc-testnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"api": "https://dn-dev.orionprotocol.io/eth-ropsten"
|
||||
"api": "https://dn-dev.orionprotocol.io/eth-ropsten",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kucoin-production": {
|
||||
"analyticsAPI": "https://trade.orionprotocol.io/api/stats",
|
||||
"referralAPI": "https://trade.orionprotocol.io/referral-api",
|
||||
"networks": {
|
||||
"1": {
|
||||
"api": "https://trade.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
},
|
||||
"liquidityMigratorAddress": "0x23a1820a47BcD022E29f6058a5FD224242F50D1A"
|
||||
},
|
||||
"56": {
|
||||
"api": "https://trade-exp.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"250": {
|
||||
"api": "https://trade-ftm.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"137": {
|
||||
"api": "https://trade-polygon.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"66": {
|
||||
"api": "https://trade-okc.orionprotocol.io",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
import { z } from 'zod';
|
||||
import { SupportedChainId } from '../../types';
|
||||
|
||||
export const pureEnvNetworksSchema = z.object({
|
||||
api: z.string(),
|
||||
services: z.object({
|
||||
blockchain: z.object({
|
||||
http: z.string(),
|
||||
}),
|
||||
aggregator: z.object({
|
||||
http: z.string(),
|
||||
ws: z.string(),
|
||||
}),
|
||||
priceFeed: z.object({
|
||||
all: z.string(),
|
||||
}),
|
||||
}),
|
||||
rpc: z.string().optional(),
|
||||
liquidityMigratorAddress: z.string().optional(),
|
||||
});
|
||||
|
||||
export const pureEnvPayloadSchema = z.object({
|
||||
analyticsAPI: z.string().url(),
|
||||
referralAPI: z.string().url(),
|
||||
networks: z.record(
|
||||
z.nativeEnum(SupportedChainId),
|
||||
z.object({
|
||||
api: z.string(),
|
||||
rpc: z.string().optional(),
|
||||
liquidityMigratorAddress: z.string().optional(),
|
||||
}),
|
||||
pureEnvNetworksSchema
|
||||
),
|
||||
});
|
||||
|
||||
export const pureEnvSchema = z.record(
|
||||
z.string(),
|
||||
z.enum(['production', 'staging', 'testing']).or(z.string()),
|
||||
pureEnvPayloadSchema,
|
||||
);
|
||||
|
||||
@@ -7,9 +7,9 @@ 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";
|
||||
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
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ export default async function fetchWithValidation<DataOut, DataIn, ErrorOut, Err
|
||||
return err({
|
||||
type: 'clientErrorWithResponsePayload' as const,
|
||||
url,
|
||||
message: `Client error: ${response.status} ${response.statusText}`,
|
||||
message: `Client error: ${response.status} ${response.statusText}. Server error: ${JSON.stringify(serverError.data)}`,
|
||||
status: response.status,
|
||||
payload: serverError.data,
|
||||
});
|
||||
@@ -125,7 +125,7 @@ export default async function fetchWithValidation<DataOut, DataIn, ErrorOut, Err
|
||||
return err({
|
||||
type: 'clientErrorPayloadParseError' as const,
|
||||
url,
|
||||
message: 'Can\'t recognize error message',
|
||||
message: 'Can\'t recognize error message. Response: ' + text,
|
||||
status: response.status,
|
||||
text,
|
||||
error: serverError.error,
|
||||
@@ -134,7 +134,7 @@ export default async function fetchWithValidation<DataOut, DataIn, ErrorOut, Err
|
||||
return err({
|
||||
type: 'clientError' as const,
|
||||
url,
|
||||
message: `Error: ${response.status} ${response.statusText}`,
|
||||
message: `Error: ${response.status} ${response.statusText}. Response: ${text}`,
|
||||
status: response.status,
|
||||
text,
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ BigNumber.config({ EXPONENTIAL_AT: 1e+9 });
|
||||
|
||||
export * as config from './config';
|
||||
export { default as OrionUnit } from './OrionUnit';
|
||||
export { default as Orion } from './Orion';
|
||||
export { default as initOrionUnit } from './initOrionUnit';
|
||||
export { default as fetchWithValidation } from './fetchWithValidation';
|
||||
export { default as simpleFetch } from './simpleFetch';
|
||||
|
||||
@@ -9,22 +9,36 @@ import errorSchema from './schemas/errorSchema';
|
||||
import placeAtomicSwapSchema from './schemas/placeAtomicSwapSchema';
|
||||
import { OrionAggregatorWS } from './ws';
|
||||
import { atomicSwapHistorySchema } from './schemas/atomicSwapHistorySchema';
|
||||
import {Exchange, SignedCancelOrderRequest, SignedCFDOrder, 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';
|
||||
import httpToWS from '../../utils/httpToWS';
|
||||
|
||||
class OrionAggregator {
|
||||
private readonly apiUrl: string;
|
||||
|
||||
readonly ws: OrionAggregatorWS;
|
||||
|
||||
constructor(apiUrl: string, apiWsUrl: string) {
|
||||
this.apiUrl = apiUrl;
|
||||
this.ws = new OrionAggregatorWS(apiWsUrl);
|
||||
get api() {
|
||||
return this.apiUrl;
|
||||
}
|
||||
|
||||
constructor(
|
||||
httpAPIUrl: string,
|
||||
wsAPIUrl: string,
|
||||
) {
|
||||
// const oaUrl = new URL(apiUrl);
|
||||
// const oaWsProtocol = oaUrl.protocol === 'https:' ? 'wss' : 'ws';
|
||||
// const orionAggregatorWsUrl = `${oaWsProtocol}://${oaUrl.host + (oaUrl.pathname === '/'
|
||||
// ? ''
|
||||
// : oaUrl.pathname)}/v1`;
|
||||
|
||||
this.apiUrl = httpAPIUrl;
|
||||
this.ws = new OrionAggregatorWS(httpToWS(wsAPIUrl));
|
||||
|
||||
this.getHistoryAtomicSwaps = this.getHistoryAtomicSwaps.bind(this);
|
||||
this.getPairConfig = this.getPairConfig.bind(this);
|
||||
|
||||
@@ -68,10 +68,10 @@ type SwapInfoSubscription = {
|
||||
type AddressUpdateUpdate = {
|
||||
kind: 'update',
|
||||
balances: Partial<
|
||||
Record<
|
||||
string,
|
||||
Balance
|
||||
>
|
||||
Record<
|
||||
string,
|
||||
Balance
|
||||
>
|
||||
>,
|
||||
order?: z.infer<typeof orderUpdateSchema> | z.infer<typeof fullOrderSchema>
|
||||
}
|
||||
@@ -79,10 +79,10 @@ type AddressUpdateUpdate = {
|
||||
type AddressUpdateInitial = {
|
||||
kind: 'initial',
|
||||
balances: Partial<
|
||||
Record<
|
||||
string,
|
||||
Balance
|
||||
>
|
||||
Record<
|
||||
string,
|
||||
Balance
|
||||
>
|
||||
>,
|
||||
orders?: z.infer<typeof fullOrderSchema>[] // The field is not defined if the user has no orders
|
||||
}
|
||||
@@ -124,7 +124,23 @@ const exclusiveSubscriptions = [
|
||||
SubscriptionType.ASSET_PAIRS_CONFIG_UPDATES_SUBSCRIBE,
|
||||
] as const;
|
||||
|
||||
type WsMessage = string | ArrayBufferLike | Blob | ArrayBufferView;
|
||||
type BufferLike =
|
||||
| string
|
||||
| Buffer
|
||||
| DataView
|
||||
| number
|
||||
| ArrayBufferView
|
||||
| Uint8Array
|
||||
| ArrayBuffer
|
||||
| SharedArrayBuffer
|
||||
| ReadonlyArray<unknown>
|
||||
| ReadonlyArray<number>
|
||||
| { valueOf(): ArrayBuffer }
|
||||
| { valueOf(): SharedArrayBuffer }
|
||||
| { valueOf(): Uint8Array }
|
||||
| { valueOf(): ReadonlyArray<number> }
|
||||
| { valueOf(): string }
|
||||
| { [Symbol.toPrimitive](hint: string): string };
|
||||
|
||||
const isSubType = (subType: string): subType is keyof Subscription => Object.values(SubscriptionType).some((t) => t === subType);
|
||||
class OrionAggregatorWS {
|
||||
@@ -148,14 +164,23 @@ class OrionAggregatorWS {
|
||||
|
||||
private readonly wsUrl: string;
|
||||
|
||||
constructor(wsUrl: string, logger?: (msg: string) => void, onInit?: () => void, onError?: (err: string) => void) {
|
||||
get api() {
|
||||
return this.wsUrl;
|
||||
}
|
||||
|
||||
constructor(
|
||||
wsUrl: string,
|
||||
logger?: (msg: string) => void,
|
||||
onInit?: () => void,
|
||||
onError?: (err: string) => void
|
||||
) {
|
||||
this.wsUrl = wsUrl;
|
||||
this.logger = logger;
|
||||
this.onInit = onInit;
|
||||
this.onError = onError;
|
||||
}
|
||||
|
||||
private sendRaw(data: WsMessage) {
|
||||
private sendRaw(data: BufferLike) {
|
||||
if (this.ws?.readyState === 1) {
|
||||
this.ws.send(data);
|
||||
} else if (this.ws?.readyState === 0) {
|
||||
@@ -167,7 +192,9 @@ class OrionAggregatorWS {
|
||||
|
||||
private send(data: unknown) {
|
||||
if (this.ws?.readyState === 1) {
|
||||
this.ws.send(JSON.stringify(data));
|
||||
const jsonData = JSON.stringify(data);
|
||||
this.ws.send(jsonData);
|
||||
this.logger?.(`Sent: ${jsonData}`);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.send(data);
|
||||
@@ -268,7 +295,7 @@ class OrionAggregatorWS {
|
||||
delete this.ws;
|
||||
}
|
||||
|
||||
init(isReconnect = false) {
|
||||
private init(isReconnect = false) {
|
||||
this.isClosedIntentionally = false;
|
||||
this.ws = new WebSocket(this.wsUrl);
|
||||
this.ws.onerror = (err) => {
|
||||
@@ -455,7 +482,7 @@ class OrionAggregatorWS {
|
||||
});
|
||||
}
|
||||
break;
|
||||
case MessageType.CFD_ADDRESS_UPDATE: {
|
||||
case MessageType.CFD_ADDRESS_UPDATE:
|
||||
switch (json.k) { // message kind
|
||||
case 'i': { // initial
|
||||
const fullOrders = json.o
|
||||
@@ -494,7 +521,6 @@ class OrionAggregatorWS {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MessageType.ADDRESS_UPDATE: {
|
||||
const balances = json.b
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { z } from "zod";
|
||||
import { fullOrderSchema, orderUpdateSchema } from "./addressUpdateSchema";
|
||||
import baseMessageSchema from "./baseMessageSchema";
|
||||
import MessageType from "../MessageType";
|
||||
import cfdBalancesSchema from "./cfdBalancesSchema";
|
||||
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(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import positionStatuses from "../../../../constants/positionStatuses";
|
||||
import positionStatuses from '../../../../constants/positionStatuses';
|
||||
|
||||
const cfdBalanceSchema = z
|
||||
.object({
|
||||
|
||||
@@ -10,8 +10,12 @@ export default class OrionAnalytics {
|
||||
this.getOverview = this.getOverview.bind(this);
|
||||
}
|
||||
|
||||
get api() {
|
||||
return this.apiUrl;
|
||||
}
|
||||
|
||||
getOverview = () => fetchWithValidation(
|
||||
`${this.apiUrl}/api/stats/overview`,
|
||||
`${this.apiUrl}/overview`,
|
||||
overviewSchema,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,10 @@ type CfdHistoryQuery = {
|
||||
class OrionBlockchain {
|
||||
private readonly apiUrl: string;
|
||||
|
||||
get api() {
|
||||
return this.apiUrl;
|
||||
}
|
||||
|
||||
constructor(apiUrl: string) {
|
||||
this.apiUrl = apiUrl;
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ class PriceFeed {
|
||||
|
||||
readonly ws: PriceFeedWS;
|
||||
|
||||
get api() {
|
||||
return this.apiUrl;
|
||||
}
|
||||
|
||||
constructor(apiUrl: string) {
|
||||
this.apiUrl = apiUrl;
|
||||
this.ws = new PriceFeedWS(this.wsUrl);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import fetchWithValidation from '../../fetchWithValidation';
|
||||
import { errorSchema, miniStatsSchema, rewardsMappingSchema } from './schemas';
|
||||
import distinctAnalyticsSchema from './schemas/distinctAnalyticsSchema';
|
||||
@@ -21,8 +22,12 @@ type SignatureType = {
|
||||
class ReferralSystem {
|
||||
private apiUrl: string;
|
||||
|
||||
constructor(apiUrl: string, env: string) {
|
||||
this.apiUrl = ReferralSystem.getActualApiUrl(apiUrl, env);
|
||||
get api() {
|
||||
return this.apiUrl;
|
||||
}
|
||||
|
||||
constructor(apiUrl: string) {
|
||||
this.apiUrl = apiUrl;
|
||||
|
||||
this.getLink = this.getLink.bind(this);
|
||||
this.getDistinctAnalytics = this.getDistinctAnalytics.bind(this);
|
||||
@@ -31,19 +36,6 @@ class ReferralSystem {
|
||||
this.getMyReferral = this.getMyReferral.bind(this);
|
||||
}
|
||||
|
||||
// ресурсы реферальной системы в тестинг окружении имеют вид
|
||||
// testing.orionprotocol.io/referral-api вместо обычного
|
||||
// testing.orionprotocol.io/bsc-testnet/referral-api, поэтому лишняя часть вырезается
|
||||
static getActualApiUrl = (apiUrl: string, env: string) => {
|
||||
if (env === 'testing' || env === 'custom') {
|
||||
const { protocol, hostname } = new URL(apiUrl);
|
||||
|
||||
return `${protocol}//${hostname}/referral-api`;
|
||||
}
|
||||
|
||||
return `${apiUrl}/referral-api`;
|
||||
};
|
||||
|
||||
getLink = (refererAddress: string) => fetchWithValidation(`${this.apiUrl}/referer/view/link`, linkSchema, {
|
||||
headers: {
|
||||
'referer-address': refererAddress,
|
||||
@@ -76,6 +68,9 @@ class ReferralSystem {
|
||||
globalAnalyticsSchema,
|
||||
);
|
||||
|
||||
/**
|
||||
* @param refererAddress Address without 0x prefix
|
||||
*/
|
||||
getMiniStats = (refererAddress: string) => fetchWithValidation(
|
||||
`${this.apiUrl}/referer/view/mini-latest-stats`,
|
||||
miniStatsSchema,
|
||||
@@ -84,6 +79,10 @@ class ReferralSystem {
|
||||
'referer-address': refererAddress,
|
||||
},
|
||||
},
|
||||
z.object({
|
||||
status: z.string(),
|
||||
message: z.string(),
|
||||
}),
|
||||
);
|
||||
|
||||
getRewardsMapping = (referralAddress: string) => fetchWithValidation(
|
||||
|
||||
57
src/types.ts
57
src/types.ts
@@ -3,20 +3,24 @@ import exchanges from './constants/exchanges';
|
||||
import subOrderStatuses from './constants/subOrderStatuses';
|
||||
import positionStatuses from './constants/positionStatuses';
|
||||
|
||||
export type DeepPartial<T> = T extends object ? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
} : T;
|
||||
|
||||
export type AssetPairUpdate = {
|
||||
minQty: number,
|
||||
pricePrecision: number,
|
||||
minQty: number,
|
||||
pricePrecision: number,
|
||||
}
|
||||
export type SubOrder = {
|
||||
pair: string,
|
||||
exchange: string,
|
||||
id: number,
|
||||
amount: number,
|
||||
settledAmount: number,
|
||||
price: number,
|
||||
status: typeof subOrderStatuses[number],
|
||||
side: 'BUY' | 'SELL',
|
||||
subOrdQty: number
|
||||
pair: string,
|
||||
exchange: string,
|
||||
id: number,
|
||||
amount: number,
|
||||
settledAmount: number,
|
||||
price: number,
|
||||
status: typeof subOrderStatuses[number],
|
||||
side: 'BUY' | 'SELL',
|
||||
subOrdQty: number
|
||||
}
|
||||
|
||||
export type Balance = {
|
||||
@@ -238,3 +242,34 @@ export enum HistoryTransactionStatus {
|
||||
APPROVING = 'Approving',
|
||||
CANCELLED = 'Cancelled',
|
||||
}
|
||||
|
||||
export type VerboseOrionUnitConfig = {
|
||||
// env?: string;
|
||||
// api: string;
|
||||
chainId: SupportedChainId;
|
||||
nodeJsonRpc: string;
|
||||
services: {
|
||||
orionBlockchain: {
|
||||
http: string;
|
||||
// For example:
|
||||
// http://localhost:3001/,
|
||||
// http://10.123.34.23:3001/,
|
||||
// https://blockchain.orionprotocol.io/
|
||||
},
|
||||
orionAggregator: {
|
||||
http: string;
|
||||
ws: string;
|
||||
// For example:
|
||||
// http://localhost:3002/,
|
||||
// http://10.34.23.5:3002/,
|
||||
// shttps://aggregator.orionprotocol.io/
|
||||
},
|
||||
priceFeed: {
|
||||
api: string;
|
||||
// For example:
|
||||
// http://localhost:3003/,
|
||||
// http://10.23.5.11:3003/,
|
||||
// https://price-feed.orionprotocol.io/
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const arrayEquals = (a: unknown[], b: unknown[]) => a.length === b.length
|
||||
&& a.every((value, index) => value === b[index]);
|
||||
const arrayEquals = (a: unknown[], b: unknown[]) => a.length === b.length &&
|
||||
a.every((value, index) => value === b[index]);
|
||||
|
||||
export default arrayEquals;
|
||||
|
||||
17
src/utils/httpToWS.ts
Normal file
17
src/utils/httpToWS.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
const httpToWS = (url: string) => {
|
||||
let parsedUrl: URL;
|
||||
try {
|
||||
parsedUrl = new URL(url);
|
||||
} catch (e) {
|
||||
console.error(`httpToWS: Invalid URL ${url}`);
|
||||
throw e;
|
||||
}
|
||||
if (parsedUrl.protocol === 'https:') {
|
||||
parsedUrl.protocol = 'wss:';
|
||||
} else if (parsedUrl.protocol === 'http:') {
|
||||
parsedUrl.protocol = 'ws:';
|
||||
}
|
||||
return parsedUrl.toString();
|
||||
};
|
||||
|
||||
export default httpToWS;
|
||||
@@ -9,6 +9,6 @@ export default function isNetworkCodeInEnvironment(networkCode: string, env: str
|
||||
if (envNetworks === undefined) throw new Error('Env networks is undefined (isNetworkCodeInEnvironment)');
|
||||
|
||||
return Object.values(chains)
|
||||
.some((chain) => chain.code.toLowerCase() === networkCode.toLowerCase()
|
||||
&& chain.chainId in envNetworks);
|
||||
.some((chain) => chain.code.toLowerCase() === networkCode.toLowerCase() &&
|
||||
chain.chainId in envNetworks);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
// "extends": "@tsconfig/strictest/tsconfig.json",
|
||||
"files": [
|
||||
"./src/index.ts"
|
||||
],
|
||||
"include": [
|
||||
"./src/**/*.ts"
|
||||
"./src/**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/__tests__/*",
|
||||
// "**/__tests__/*",
|
||||
"lib"
|
||||
],
|
||||
"compilerOptions": {
|
||||
|
||||
Reference in New Issue
Block a user