mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-03-14 06:02:36 +03:00
Merge branch 'feature/generic-swap-support' into feature/swap-info-eps-field
# Conflicts: # package-lock.json # package.json
This commit is contained in:
4277
package-lock.json
generated
4277
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@orionprotocol/sdk",
|
||||
"version": "0.19.48-dev.5-rc-0",
|
||||
"version": "0.19.48-dev.6-rc-0",
|
||||
"description": "Orion Protocol SDK",
|
||||
"main": "./lib/index.cjs",
|
||||
"module": "./lib/index.js",
|
||||
@@ -10,6 +10,9 @@
|
||||
"require": "./lib/index.cjs",
|
||||
"import": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts"
|
||||
},
|
||||
"./browser": {
|
||||
"browser": "./lib/index.global.js"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
@@ -27,6 +30,7 @@
|
||||
"coverage": "jest --coverage",
|
||||
"lint:eslint": "eslint ./src --ext .ts,.js,.tsx,.jsx",
|
||||
"lint:eslint:fix": "eslint ./src --ext .ts,.js,.tsx,.jsx --fix",
|
||||
"postinstall": "patch-package",
|
||||
"postpublish": "npm run publish-npm",
|
||||
"publish-npm": "npm publish --access public --ignore-scripts --@orionprotocol:registry='https://registry.npmjs.org'",
|
||||
"test": "dotenv jest",
|
||||
@@ -51,23 +55,23 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.4",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
||||
"@tsconfig/esm": "^1.0.3",
|
||||
"@tsconfig/esm": "^1.0.4",
|
||||
"@tsconfig/strictest": "^2.0.1",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/node": "^20.2.3",
|
||||
"@types/node": "^20.5.1",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@types/ws": "^8.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||
"@typescript-eslint/parser": "^5.59.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.4.0",
|
||||
"@typescript-eslint/parser": "^6.4.0",
|
||||
"babel-loader": "^9.1.2",
|
||||
"concurrently": "^8.1.0",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-config-standard-with-typescript": "^34.0.1",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-n": "^15.7.0",
|
||||
"eslint-config-standard-with-typescript": "^38.0.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-n": "^16.0.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"http-terminator": "^3.2.0",
|
||||
"husky": "^8.0.3",
|
||||
@@ -76,14 +80,14 @@
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-loader": "^9.4.3",
|
||||
"ts-node": "github:TypeStrong/ts-node#main",
|
||||
"tsup": "^6.7.0",
|
||||
"typescript": "^5.1.3"
|
||||
"tsup": "^7.2.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0",
|
||||
"@ethersproject/abstract-signer": "^5.7.0",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
"@orionprotocol/contracts": "1.16.1",
|
||||
"@orionprotocol/contracts": "1.16.2",
|
||||
"bignumber.js": "^9.1.1",
|
||||
"bson-objectid": "^2.0.4",
|
||||
"buffer": "^6.0.3",
|
||||
@@ -93,7 +97,8 @@
|
||||
"just-clone": "^6.2.0",
|
||||
"merge-anything": "^5.1.7",
|
||||
"neverthrow": "^6.0.0",
|
||||
"simple-typed-fetch": "0.2.1",
|
||||
"patch-package": "^8.0.0",
|
||||
"simple-typed-fetch": "0.2.2",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"tiny-invariant": "^1.3.1",
|
||||
"ts-is-present": "^1.2.2",
|
||||
|
||||
15
patches/unfetch+5.0.0.patch
Normal file
15
patches/unfetch+5.0.0.patch
Normal file
@@ -0,0 +1,15 @@
|
||||
diff --git a/node_modules/unfetch/package.json b/node_modules/unfetch/package.json
|
||||
index ec47dc2..33954f7 100644
|
||||
--- a/node_modules/unfetch/package.json
|
||||
+++ b/node_modules/unfetch/package.json
|
||||
@@ -15,8 +15,8 @@
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
- "import": "./index.mjs",
|
||||
- "default": "./index.js"
|
||||
+ "import": "./dist/unfetch.mjs",
|
||||
+ "default": "./dist/unfetch.js"
|
||||
},
|
||||
"./polyfill": {
|
||||
"default": "./polyfill/index.js"
|
||||
@@ -130,19 +130,19 @@ const getHistory = async (units: Unit[], address: string, limit = 1000) => {
|
||||
asset: string
|
||||
sender: string
|
||||
secretHash: string
|
||||
receiver: string | undefined
|
||||
secret: string | undefined
|
||||
receiver?: string | undefined
|
||||
secret?: string | undefined
|
||||
timestamp: TargetItem['timestamp'] & SourceItem['timestamp']
|
||||
expiration: TargetItem['expiration'] & SourceItem['expiration']
|
||||
transactions: TargetItem['transactions'] & SourceItem['transactions']
|
||||
lockOrder: AggItem['lockOrder'] | undefined
|
||||
redeemOrder: AggItem['redeemOrder'] | undefined
|
||||
lockOrder?: AggItem['lockOrder'] | undefined
|
||||
redeemOrder?: AggItem['redeemOrder'] | undefined
|
||||
amountToReceive: SourceItem['amountToReceive']
|
||||
amountToSpend: SourceItem['amountToSpend']
|
||||
status: {
|
||||
source: SourceItem['state']
|
||||
target: TargetItem['state'] | undefined
|
||||
aggregator: AggItem['status'] | undefined
|
||||
source?: SourceItem['state'] | undefined
|
||||
target?: TargetItem['state'] | undefined
|
||||
aggregator?: AggItem['status'] | undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,78 +1,267 @@
|
||||
import type { ethers } from 'ethers';
|
||||
import {
|
||||
type AtomicSwap, type SupportedChainId,
|
||||
type Unit, INTERNAL_PROTOCOL_PRECISION
|
||||
import { ethers } from 'ethers';
|
||||
import type {
|
||||
Unit, AtomicSwapLocal, SupportedChainId, TransactionInfo, AtomicSwap, RedeemOrder
|
||||
} from '../../index.js';
|
||||
import getHistoryExt from './getHistory.js';
|
||||
import { INTERNAL_PROTOCOL_PRECISION, TxStatus, TxType } from '../../index.js';
|
||||
import getHistory from './getHistory.js';
|
||||
import swapExt from './swap.js';
|
||||
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import generateSecret from '../../utils/generateSecret.js';
|
||||
import { isPresent } from 'ts-is-present';
|
||||
import { invariant } from '../../utils/invariant.js';
|
||||
import { simpleFetch } from 'simple-typed-fetch';
|
||||
|
||||
export const SECONDS_IN_DAY = 60 * 60 * 24;
|
||||
export const EXPIRATION_DAYS = 4;
|
||||
|
||||
type ExternalAtomicsData = Awaited<ReturnType<typeof getHistory>>;
|
||||
export default class Bridge {
|
||||
readonly EXTERNAL_ATOMICS_DATA_CACHE_TIME_MS = 5 * 1000; // 5 seconds
|
||||
private externalAtomicSwaps: Partial<Record<string, { // wallet address -> data
|
||||
lastUpdate: number
|
||||
data: ExternalAtomicsData
|
||||
}>> = {};
|
||||
|
||||
readonly ADDRESS_TO_ASSET_CACHE_TIME_MS = 5 * 60 * 1000; // 5 minutes
|
||||
private addressToAsset: {
|
||||
lastUpdate: number
|
||||
data: Partial<Record<string, Partial<Record<SupportedChainId, string>>>>
|
||||
} = { lastUpdate: 0, data: {} };
|
||||
|
||||
constructor(
|
||||
private readonly unitsArray: Unit[],
|
||||
) {}
|
||||
|
||||
async getMergedHistory(
|
||||
externalStoredAtomicSwaps: AtomicSwap[],
|
||||
walletAddress: string,
|
||||
registerRedeemOrder(
|
||||
secretHash: string,
|
||||
redeemOrder: RedeemOrder,
|
||||
) {
|
||||
const bridgeHistory = await this.getHistory(walletAddress);
|
||||
|
||||
return Object.values(bridgeHistory).map((atomicSwap) => {
|
||||
if (atomicSwap === undefined) throw new Error('No atomic swap');
|
||||
|
||||
const {
|
||||
secretHash,
|
||||
amountToReceive,
|
||||
amountToSpend,
|
||||
targetChainId,
|
||||
asset,
|
||||
sourceChainId,
|
||||
sender,
|
||||
transactions,
|
||||
expiration,
|
||||
creationDate,
|
||||
} = atomicSwap;
|
||||
|
||||
const localSwap = externalStoredAtomicSwaps.find(
|
||||
(swap) => secretHash === swap.secretHash,
|
||||
);
|
||||
|
||||
const amount = amountToReceive ?? amountToSpend ?? 0;
|
||||
|
||||
// Checking if transaction hash from blockchain is different from the same in local storage
|
||||
// and changing it to the correct one
|
||||
|
||||
let assetName = asset;
|
||||
|
||||
// LEGACY. Some old atomic swaps have address instead of asset name. Here we handle this case
|
||||
if (asset.includes('0x')) {
|
||||
assetName = '—'; // We don't want to display address even if we can't find asset name
|
||||
const senderCached = this.externalAtomicSwaps[redeemOrder.sender];
|
||||
const receiverCached = this.externalAtomicSwaps[redeemOrder.receiver];
|
||||
if (senderCached !== undefined) {
|
||||
const atomic = senderCached.data[secretHash];
|
||||
if (atomic !== undefined) {
|
||||
atomic.redeemOrder = redeemOrder;
|
||||
}
|
||||
|
||||
return {
|
||||
localSwap,
|
||||
sourceNetwork: sourceChainId,
|
||||
targetNetwork: targetChainId,
|
||||
amount: new BigNumber(amount)
|
||||
.multipliedBy(new BigNumber(10).pow(INTERNAL_PROTOCOL_PRECISION))
|
||||
.toString(),
|
||||
walletAddress: sender,
|
||||
secretHash,
|
||||
lockTransactionHash: transactions?.lock,
|
||||
refundTransactionHash: transactions?.refund,
|
||||
asset: assetName,
|
||||
expiration:
|
||||
expiration?.lock ?? creationDate.getTime() + 60 * 60 * 24 * 4, // Or default 4 days
|
||||
creationDate: creationDate.getTime(),
|
||||
redeemOrder: atomicSwap.redeemOrder,
|
||||
};
|
||||
});
|
||||
}
|
||||
if (receiverCached !== undefined) {
|
||||
const atomic = receiverCached.data[secretHash];
|
||||
if (atomic !== undefined) {
|
||||
atomic.redeemOrder = redeemOrder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getHistory(address: string, limit = 1000) {
|
||||
return getHistoryExt(this.unitsArray, address, limit);
|
||||
async getCombinedAddressToAsset() {
|
||||
const { lastUpdate, data } = this.addressToAsset;
|
||||
const cacheIsExpired = lastUpdate + this.EXTERNAL_ATOMICS_DATA_CACHE_TIME_MS < Date.now();
|
||||
if (!cacheIsExpired) return data;
|
||||
const addressToAssetData: Partial<Record<string, Partial<Record<SupportedChainId, string>>>> = {};
|
||||
await Promise.all(this.unitsArray.map(async (unit) => {
|
||||
const { blockchainService, chainId } = unit;
|
||||
const { assetToAddress } = await simpleFetch(blockchainService.getInfo)();
|
||||
Object.entries(assetToAddress).forEach(([asset, address]) => {
|
||||
if (address !== undefined) {
|
||||
const assetRecord = addressToAssetData[address];
|
||||
if (assetRecord !== undefined) {
|
||||
assetRecord[chainId] = asset;
|
||||
} else {
|
||||
addressToAssetData[address] = {
|
||||
[chainId]: asset,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
this.addressToAsset = {
|
||||
lastUpdate: Date.now(),
|
||||
data: addressToAssetData,
|
||||
}
|
||||
return addressToAssetData;
|
||||
}
|
||||
|
||||
async combineLocalAndExternalData(
|
||||
walletAddress: string,
|
||||
localAtomicSwaps: AtomicSwapLocal[],
|
||||
transactions: TransactionInfo[],
|
||||
) {
|
||||
// Prepare transactions data
|
||||
const byTxHashMap = new Map<string, TransactionInfo>();
|
||||
type BridgeTxs = {
|
||||
lockTx?: TransactionInfo | undefined
|
||||
redeemTx?: TransactionInfo | undefined
|
||||
refundTx?: TransactionInfo | undefined
|
||||
};
|
||||
const bySecretHashMap = new Map<string, BridgeTxs>();
|
||||
transactions.forEach((tx) => {
|
||||
if (tx.hash !== undefined) byTxHashMap.set(tx.hash, tx);
|
||||
if (tx.payload) {
|
||||
const { type, data } = tx.payload;
|
||||
if (type === TxType.BRIDGE_LOCK || type === TxType.BRIDGE_REDEEM || type === TxType.BRIDGE_REFUND) {
|
||||
const item = bySecretHashMap.get(data.secretHash);
|
||||
bySecretHashMap.set(data.secretHash, {
|
||||
...item,
|
||||
lockTx: type === TxType.BRIDGE_LOCK ? tx : item?.lockTx,
|
||||
redeemTx: type === TxType.BRIDGE_REDEEM ? tx : item?.redeemTx,
|
||||
refundTx: type === TxType.BRIDGE_REFUND ? tx : item?.refundTx,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Combine local data and external data
|
||||
const bridgeHistory = await this.getHistory(walletAddress);
|
||||
const combinedAddressToAsset = await this.getCombinedAddressToAsset();
|
||||
const atomicSwapsMap = new Map<string, AtomicSwap>();
|
||||
Object.values(bridgeHistory)
|
||||
.filter(isPresent)
|
||||
.forEach((atomicHistoryItem) => {
|
||||
const { lock, redeem, refund } = atomicHistoryItem.transactions ?? {};
|
||||
const lockTx = lock !== undefined
|
||||
? {
|
||||
hash: lock,
|
||||
status: TxStatus.SETTLED,
|
||||
}
|
||||
: undefined;
|
||||
const redeemTx = redeem !== undefined
|
||||
? {
|
||||
hash: redeem,
|
||||
status: TxStatus.SETTLED,
|
||||
}
|
||||
: undefined;
|
||||
const refundTx = refund !== undefined
|
||||
? {
|
||||
hash: refund,
|
||||
status: TxStatus.SETTLED,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
let redeemExpired = false;
|
||||
|
||||
// If redeem order is expired
|
||||
if (atomicHistoryItem.redeemOrder) {
|
||||
const currentTime = Date.now();
|
||||
if (currentTime > atomicHistoryItem.redeemOrder.expiration) redeemExpired = true;
|
||||
}
|
||||
|
||||
const assetName = combinedAddressToAsset[atomicHistoryItem.asset]?.[atomicHistoryItem.sourceChainId];
|
||||
|
||||
const amount = atomicHistoryItem.amountToReceive ?? atomicHistoryItem.amountToSpend;
|
||||
|
||||
invariant(atomicHistoryItem.expiration?.lock, 'Lock expiration must be defined');
|
||||
atomicSwapsMap.set(atomicHistoryItem.secretHash, {
|
||||
...atomicHistoryItem,
|
||||
walletAddress: atomicHistoryItem.sender,
|
||||
creationDate: atomicHistoryItem.creationDate.getTime(),
|
||||
assetName,
|
||||
lockTx,
|
||||
amount: amount !== undefined ? amount.toString() : undefined,
|
||||
redeemTx,
|
||||
refundTx,
|
||||
lockExpiration: atomicHistoryItem.expiration.lock,
|
||||
redeemExpired,
|
||||
});
|
||||
});
|
||||
localAtomicSwaps.forEach((atomic) => {
|
||||
const atomicInMap = atomicSwapsMap.get(atomic.secretHash);
|
||||
|
||||
const { liquidityMigrationTxHash, redeemSettlement, secretHash } = atomic;
|
||||
|
||||
const secretHashTxs = bySecretHashMap.get(secretHash);
|
||||
let redeemTx: TransactionInfo | undefined;
|
||||
if (redeemSettlement) {
|
||||
if (redeemSettlement.type === 'own_tx') {
|
||||
redeemTx = secretHashTxs?.redeemTx;
|
||||
} else if (redeemSettlement.result) {
|
||||
redeemTx = {
|
||||
status: redeemSettlement.result.value === 'success' ? TxStatus.SETTLED : TxStatus.FAILED,
|
||||
}
|
||||
} else if (redeemSettlement.requestedAt !== undefined) {
|
||||
redeemTx = {
|
||||
status: TxStatus.PENDING,
|
||||
}
|
||||
}
|
||||
}
|
||||
const liquidityMigrationTx = liquidityMigrationTxHash !== undefined ? byTxHashMap.get(liquidityMigrationTxHash) : undefined;
|
||||
const amount = atomic.amount !== undefined
|
||||
? new BigNumber(atomic.amount).div(10 ** INTERNAL_PROTOCOL_PRECISION).toString()
|
||||
: undefined;
|
||||
if (atomicInMap) { // Merge local and backend data
|
||||
atomicSwapsMap.set(atomic.secretHash, {
|
||||
...atomicInMap,
|
||||
...atomic,
|
||||
lockExpiration: atomicInMap.lockExpiration,
|
||||
targetChainId: atomicInMap.targetChainId,
|
||||
sourceChainId: atomicInMap.sourceChainId,
|
||||
amount: atomicInMap.amount ?? amount,
|
||||
lockTx: atomicInMap.lockTx ?? secretHashTxs?.lockTx,
|
||||
redeemTx: atomicInMap.redeemTx ?? redeemTx,
|
||||
refundTx: atomicInMap.refundTx ?? secretHashTxs?.refundTx,
|
||||
liquidityMigrationTx: atomicInMap.liquidityMigrationTx ?? liquidityMigrationTx,
|
||||
});
|
||||
} else {
|
||||
invariant(atomic.targetChainId, 'Target chain id is not defined');
|
||||
invariant(atomic.sourceChainId, 'Source chain id is not defined');
|
||||
invariant(atomic.lockExpiration, 'Lock expiration is not defined');
|
||||
|
||||
atomicSwapsMap.set(atomic.secretHash, {
|
||||
...atomic,
|
||||
sourceChainId: atomic.sourceChainId,
|
||||
targetChainId: atomic.targetChainId,
|
||||
lockExpiration: atomic.lockExpiration,
|
||||
lockTx: secretHashTxs?.lockTx,
|
||||
redeemTx,
|
||||
refundTx: secretHashTxs?.refundTx,
|
||||
liquidityMigrationTx,
|
||||
});
|
||||
}
|
||||
});
|
||||
return atomicSwapsMap;
|
||||
}
|
||||
|
||||
makeAtomicSwap(
|
||||
walletAddress: string,
|
||||
networkFrom: SupportedChainId,
|
||||
networkTo: SupportedChainId,
|
||||
amount: string,
|
||||
asset: string,
|
||||
env?: string | undefined,
|
||||
) {
|
||||
const secret = generateSecret();
|
||||
const secretHash = ethers.utils.keccak256(secret);
|
||||
const lockExpiration = Date.now() + SECONDS_IN_DAY * EXPIRATION_DAYS * 1000;
|
||||
|
||||
return {
|
||||
sourceChainId: networkFrom,
|
||||
targetChainId: networkTo,
|
||||
amount,
|
||||
walletAddress,
|
||||
secret,
|
||||
secretHash,
|
||||
assetName: asset,
|
||||
creationDate: Date.now(),
|
||||
lockExpiration,
|
||||
env,
|
||||
};
|
||||
}
|
||||
|
||||
async getHistory(address: string, limit = 1000) {
|
||||
const cachedData = this.externalAtomicSwaps[address];
|
||||
let data: ExternalAtomicsData | undefined;
|
||||
if (cachedData !== undefined) {
|
||||
const cacheIsExpired = cachedData.lastUpdate + this.EXTERNAL_ATOMICS_DATA_CACHE_TIME_MS < Date.now();
|
||||
if (!cacheIsExpired) data = cachedData.data;
|
||||
}
|
||||
|
||||
if (data === undefined) {
|
||||
data = await getHistory(this.unitsArray, address, limit);
|
||||
this.externalAtomicSwaps[address] = {
|
||||
lastUpdate: Date.now(),
|
||||
data,
|
||||
};
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
swap(
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"label": "Ethereum Mainnet",
|
||||
"shortName": "ETH",
|
||||
"code": "eth",
|
||||
"rpc": "https://trade.orionprotocol.io/rpc",
|
||||
"rpc": "https://trade.orionprotocol.io/eth-mainnet/rpc",
|
||||
"baseCurrencyName": "ETH",
|
||||
"contracts": {
|
||||
"WETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
@@ -31,7 +31,7 @@
|
||||
"label": "Binance Smart Chain Testnet",
|
||||
"shortName": "BSC-Testnet",
|
||||
"code": "bsc",
|
||||
"rpc": "https://bsc-stage.node.orionprotocol.io/",
|
||||
"rpc": "https://data-seed-prebsc-1-s1.bnbchain.org:8545/",
|
||||
"baseCurrencyName": "BNB",
|
||||
"contracts": {
|
||||
"WETH": "0x23eE96bEaAB62abE126AA192e677c52bB7d274F0",
|
||||
@@ -83,7 +83,7 @@
|
||||
"label": "Fantom Testnet",
|
||||
"shortName": "FTM-Testnet",
|
||||
"code": "ftm",
|
||||
"rpc": "https://testing.orionprotocol.io/ftm-testnet/rpc",
|
||||
"rpc": "https://rpc.testnet.fantom.network/",
|
||||
"baseCurrencyName": "FTM",
|
||||
"contracts": {
|
||||
"WETH": "",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"referralAPI": "https://trade.orionprotocol.io/referral-api",
|
||||
"networks": {
|
||||
"1": {
|
||||
"api": "https://trade.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/eth-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -20,7 +20,7 @@
|
||||
"liquidityMigratorAddress": "0x23a1820a47BcD022E29f6058a5FD224242F50D1A"
|
||||
},
|
||||
"56": {
|
||||
"api": "https://trade-exp.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/bsc-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -35,7 +35,7 @@
|
||||
}
|
||||
},
|
||||
"250": {
|
||||
"api": "https://trade-ftm.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/ftm-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -50,7 +50,7 @@
|
||||
}
|
||||
},
|
||||
"137": {
|
||||
"api": "https://trade-polygon.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/polygon-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -65,7 +65,7 @@
|
||||
}
|
||||
},
|
||||
"66": {
|
||||
"api": "https://trade-okc.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/okc-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -183,7 +183,7 @@
|
||||
"referralAPI": "https://staging.orionprotocol.io/referral-api",
|
||||
"networks": {
|
||||
"1": {
|
||||
"api": "https://staging.orionprotocol.io",
|
||||
"api": "https://staging.orionprotocol.io/eth-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -198,7 +198,7 @@
|
||||
}
|
||||
},
|
||||
"56": {
|
||||
"api": "https://staging-bsc.orionprotocol.io",
|
||||
"api": "https://staging.orionprotocol.io/bsc-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -213,7 +213,7 @@
|
||||
}
|
||||
},
|
||||
"250": {
|
||||
"api": "https://staging-ftm.orionprotocol.io",
|
||||
"api": "https://staging.orionprotocol.io/ftm-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -228,7 +228,7 @@
|
||||
}
|
||||
},
|
||||
"137": {
|
||||
"api": "https://staging-polygon.orionprotocol.io",
|
||||
"api": "https://staging.orionprotocol.io/polygon-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -243,7 +243,7 @@
|
||||
}
|
||||
},
|
||||
"66": {
|
||||
"api": "https://staging-okc.orionprotocol.io",
|
||||
"api": "https://staging.orionprotocol.io/okc-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -300,7 +300,7 @@
|
||||
"referralAPI": "https://trade.orionprotocol.io/referral-api",
|
||||
"networks": {
|
||||
"1": {
|
||||
"api": "https://trade.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/eth-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -316,7 +316,7 @@
|
||||
"liquidityMigratorAddress": "0x23a1820a47BcD022E29f6058a5FD224242F50D1A"
|
||||
},
|
||||
"56": {
|
||||
"api": "https://trade-exp.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/bsc-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -331,7 +331,7 @@
|
||||
}
|
||||
},
|
||||
"250": {
|
||||
"api": "https://trade-ftm.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/ftm-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -346,7 +346,7 @@
|
||||
}
|
||||
},
|
||||
"137": {
|
||||
"api": "https://trade-polygon.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/polygon-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -361,7 +361,7 @@
|
||||
}
|
||||
},
|
||||
"66": {
|
||||
"api": "https://trade-okc.orionprotocol.io",
|
||||
"api": "https://trade.orionprotocol.io/okc-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -377,4 +377,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,14 +22,15 @@ const swapInfoBase = z.object({
|
||||
minAmountOut: z.number(),
|
||||
minAmountIn: z.number(),
|
||||
marketPrice: z.number().nullable(), // spending asset market price
|
||||
exchangeContractPath: z.array(z.object({
|
||||
pool: z.string(),
|
||||
assetIn: z.string(),
|
||||
assetOut: z.string(),
|
||||
factory: z.string(),
|
||||
})),
|
||||
alternatives: z.object({ // execution alternatives
|
||||
exchanges: z.array(z.string()),
|
||||
path: z.object({
|
||||
units: z.object({
|
||||
assetPair: z.string().toUpperCase(),
|
||||
action: z.string(),
|
||||
}).array(),
|
||||
}),
|
||||
path: z.array(z.string()),
|
||||
marketAmountOut: z.number().nullable(),
|
||||
marketAmountIn: z.number().nullable(),
|
||||
marketPrice: z.number(),
|
||||
|
||||
@@ -229,7 +229,7 @@ class AggregatorWS {
|
||||
this.logger?.(`Sent: ${jsonData}`);
|
||||
}
|
||||
|
||||
private hearbeatIntervalId: NodeJS.Timer | undefined;
|
||||
private hearbeatIntervalId: ReturnType<typeof setInterval> | undefined;
|
||||
private setupHeartbeat() {
|
||||
const heartbeat = () => {
|
||||
if (this.isAlive) {
|
||||
@@ -558,7 +558,7 @@ class AggregatorWS {
|
||||
break;
|
||||
case MessageType.AGGREGATED_ORDER_BOOK_UPDATE: {
|
||||
const { ob, S } = json;
|
||||
const mapOrderbookItems = (rawItems: typeof ob.a | typeof ob.b) => rawItems.reduce<OrderbookItem[]>((acc, item) => {
|
||||
const mapOrderbookItems = (rawItems: typeof ob.a) => rawItems.reduce<OrderbookItem[]>((acc, item) => {
|
||||
const [
|
||||
price,
|
||||
amount,
|
||||
|
||||
@@ -34,7 +34,7 @@ const sourceAtomicHistorySchemaItem = baseAtomicHistoryItem.extend({
|
||||
expiration: z.object({
|
||||
lock: z.number().optional(),
|
||||
}).optional(),
|
||||
state: z.enum(['BEFORE-LOCK', 'LOCKED', 'REFUNDED', 'CLAIMED']),
|
||||
state: z.enum(['BEFORE-LOCK', 'LOCKED', 'REFUNDED', 'CLAIMED']).optional(),
|
||||
targetChainId: z.number(),
|
||||
transactions: z.object({
|
||||
lock: z.string().optional(),
|
||||
@@ -51,7 +51,7 @@ const targetAtomicHistorySchemaItem = baseAtomicHistoryItem.extend({
|
||||
expiration: z.object({
|
||||
redeem: z.number().optional(),
|
||||
}).optional(),
|
||||
state: z.enum(['BEFORE-REDEEM', 'REDEEMED']),
|
||||
state: z.enum(['BEFORE-REDEEM', 'REDEEMED']).optional(),
|
||||
transactions: z.object({
|
||||
redeem: z.string().optional(),
|
||||
}).optional(),
|
||||
|
||||
156
src/types.ts
156
src/types.ts
@@ -3,6 +3,7 @@ import type { BigNumber } from 'bignumber.js';
|
||||
import type subOrderStatuses from './constants/subOrderStatuses.js';
|
||||
import type positionStatuses from './constants/positionStatuses.js';
|
||||
import type { knownEnvs } from './config/schemas/index.js';
|
||||
import type getHistory from './Orion/bridge/getHistory.js';
|
||||
|
||||
export type DeepPartial<T> = T extends object ? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
@@ -291,36 +292,149 @@ export type RedeemOrder = {
|
||||
claimReceiver: string
|
||||
}
|
||||
|
||||
export type AtomicSwap = {
|
||||
export interface AtomicSwapLocal {
|
||||
secret: string
|
||||
secretHash: string
|
||||
|
||||
walletAddress: string
|
||||
env?: string | undefined
|
||||
|
||||
sourceNetwork?: SupportedChainId
|
||||
targetNetwork?: SupportedChainId
|
||||
sourceChainId?: SupportedChainId | undefined
|
||||
targetChainId?: SupportedChainId | undefined
|
||||
|
||||
amount?: string
|
||||
asset?: string
|
||||
amount?: string | undefined
|
||||
assetName?: string | undefined
|
||||
|
||||
creationDate?: number
|
||||
expiration?: number
|
||||
liquidityMigrationTxHash?: string | undefined
|
||||
lockTransactionHash?: string | undefined
|
||||
refundTransactionHash?: string | undefined
|
||||
|
||||
lockTransactionHash?: string
|
||||
redeemTransactionHash?: string
|
||||
refundTransactionHash?: string
|
||||
liquidityMigrationTxHash?: string
|
||||
|
||||
redeemOrder?: RedeemOrder
|
||||
creationDate?: number | undefined
|
||||
lockExpiration?: number | undefined
|
||||
placingOrderError?: string | undefined
|
||||
redeemSettlement?: {
|
||||
type: 'own_tx'
|
||||
} | {
|
||||
type: 'orion_tx'
|
||||
requestedAt?: number
|
||||
result?: {
|
||||
timestamp: number
|
||||
value: 'success' | 'failed'
|
||||
}
|
||||
} | undefined
|
||||
}
|
||||
|
||||
export type ExternalStorage = {
|
||||
bridge: {
|
||||
getAtomicSwaps: () => AtomicSwap[]
|
||||
setAtomicSwaps: (atomics: AtomicSwap[]) => void
|
||||
addAtomicSwap: (atomic: AtomicSwap) => void
|
||||
updateAtomicSwap: (secretHash: string, atomic: Partial<AtomicSwap>) => void
|
||||
removeAtomicSwaps: (secretHashes: string[]) => void
|
||||
export enum TxStatus {
|
||||
QUEUED = 'queued',
|
||||
SIGN_FAILED = 'sign_failed',
|
||||
GAS_ESTIMATING = 'gas_estimating',
|
||||
ESTIMATE_GAS_FAILED = 'estimate_gas_failed',
|
||||
CANCELLED = 'cancelled',
|
||||
PENDING = 'pending',
|
||||
FAILED = 'failed',
|
||||
SETTLED = 'settled',
|
||||
SIGNING = 'signing',
|
||||
UNKNOWN = 'unknown',
|
||||
}
|
||||
|
||||
export enum TxType {
|
||||
SWAP_THROUGH_ORION_POOL = 'SWAP_THROUGH_ORION_POOL',
|
||||
DEPOSIT = 'DEPOSIT',
|
||||
WITHDRAW = 'WITHDRAW',
|
||||
BRIDGE_LOCK = 'BRIDGE_LOCK',
|
||||
BRIDGE_REDEEM = 'BRIDGE_REDEEM',
|
||||
BRIDGE_REFUND = 'BRIDGE_REFUND',
|
||||
LIQUIDITY_MIGRATION = 'LIQUIDITY_MIGRATION',
|
||||
REDEEM_TWO_ATOMICS = 'REDEEM_TWO_ATOMICS',
|
||||
}
|
||||
|
||||
export type TxDepositOrWithdrawPayload = {
|
||||
type: TxType.DEPOSIT | TxType.WITHDRAW
|
||||
data: {
|
||||
asset: string
|
||||
amount: string
|
||||
}
|
||||
};
|
||||
|
||||
export type TxSwapThroughOrionPoolPayload = {
|
||||
type: TxType.SWAP_THROUGH_ORION_POOL
|
||||
data: {
|
||||
side: 'buy' | 'sell'
|
||||
assetIn: string
|
||||
assetOut: string
|
||||
amount: string
|
||||
price: string
|
||||
}
|
||||
};
|
||||
|
||||
export type TxBridgePayload = {
|
||||
type: TxType.BRIDGE_LOCK | TxType.BRIDGE_REDEEM | TxType.BRIDGE_REFUND
|
||||
data: {
|
||||
secretHash: string
|
||||
}
|
||||
}
|
||||
|
||||
export type TxLiquidityMigrationPayload = {
|
||||
type: TxType.LIQUIDITY_MIGRATION
|
||||
data: {
|
||||
source: SupportedChainId
|
||||
target: SupportedChainId
|
||||
pair: string
|
||||
pairAddress: string
|
||||
assetA: {
|
||||
amount: string
|
||||
secretHash: string
|
||||
secret: string
|
||||
}
|
||||
assetB: {
|
||||
amount: string
|
||||
secretHash: string
|
||||
secret: string
|
||||
}
|
||||
expiration: number
|
||||
env?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type TxRedeemTwoAtomicsPayload = {
|
||||
type: TxType.REDEEM_TWO_ATOMICS
|
||||
data: {
|
||||
secretHash1: string
|
||||
secretHash2: string
|
||||
}
|
||||
}
|
||||
|
||||
export type TransactionInfo = {
|
||||
id?: string
|
||||
status?: TxStatus
|
||||
hash?: string
|
||||
payload?: TxDepositOrWithdrawPayload
|
||||
| TxSwapThroughOrionPoolPayload
|
||||
| TxBridgePayload
|
||||
| TxLiquidityMigrationPayload
|
||||
| TxRedeemTwoAtomicsPayload
|
||||
}
|
||||
|
||||
type BridgeHistory = Awaited<ReturnType<typeof getHistory>>;
|
||||
|
||||
type BridgeHistoryItem = NonNullable<BridgeHistory[string]>;
|
||||
|
||||
export type AtomicSwap = Partial<
|
||||
Omit<BridgeHistoryItem, 'creationDate' | 'expiration' | 'secret'>
|
||||
> & Partial<
|
||||
Omit<AtomicSwapLocal, 'creationDate' | 'expiration' | 'secret'>
|
||||
> & {
|
||||
sourceChainId: SupportedChainId
|
||||
targetChainId: SupportedChainId
|
||||
lockExpiration: number
|
||||
secretHash: string
|
||||
walletAddress: string
|
||||
secret?: string | undefined
|
||||
|
||||
creationDate?: number | undefined
|
||||
redeemExpired?: boolean | undefined
|
||||
|
||||
lockTx?: TransactionInfo | undefined
|
||||
redeemTx?: TransactionInfo | undefined
|
||||
refundTx?: TransactionInfo | undefined
|
||||
liquidityMigrationTx?: TransactionInfo | undefined
|
||||
}
|
||||
|
||||
18
src/utils/invariant.ts
Normal file
18
src/utils/invariant.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export function invariant<T = unknown>(
|
||||
condition: T,
|
||||
errorMessage?: ((condition: T) => string) | string,
|
||||
): asserts condition {
|
||||
if (condition) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof errorMessage === 'undefined') {
|
||||
throw new Error('Invariant failed');
|
||||
}
|
||||
|
||||
if (typeof errorMessage === 'string') {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
throw new Error(errorMessage(condition));
|
||||
}
|
||||
@@ -1,12 +1,47 @@
|
||||
import { defineConfig } from 'tsup'
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
sourcemap: true,
|
||||
platform: 'neutral',
|
||||
outDir: 'lib',
|
||||
format: ['esm', 'cjs'],
|
||||
dts: true,
|
||||
clean: true,
|
||||
shims: true,
|
||||
})
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: ['src/index.ts'],
|
||||
sourcemap: true,
|
||||
platform: 'neutral',
|
||||
minify: true,
|
||||
outDir: 'lib',
|
||||
format: ['esm', 'cjs'],
|
||||
dts: true,
|
||||
clean: true,
|
||||
shims: true,
|
||||
},
|
||||
{
|
||||
entry: ['src/index.ts'],
|
||||
globalName: 'orion',
|
||||
sourcemap: true,
|
||||
platform: 'browser',
|
||||
minify: true,
|
||||
outDir: 'lib',
|
||||
format: 'iife',
|
||||
dts: true,
|
||||
clean: true,
|
||||
shims: true,
|
||||
|
||||
// Suppress all 'node:' imports
|
||||
esbuildPlugins: [
|
||||
{
|
||||
name: 'resolve-node-polyfill',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^node:/ }, (args) => {
|
||||
return {
|
||||
path: args.path,
|
||||
namespace: 'node-polyfill',
|
||||
}
|
||||
})
|
||||
build.onLoad({ filter: /.*/, namespace: 'node-polyfill' }, (args) => {
|
||||
return {
|
||||
contents: 'undefined',
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
])
|
||||
Reference in New Issue
Block a user