Merge branch 'feature/generic-swap-support' into feature/swap-info-eps-field

# Conflicts:
#	package-lock.json
#	package.json
This commit is contained in:
Dmitriy Pavlov
2023-08-24 10:34:31 +03:00
13 changed files with 3202 additions and 1734 deletions

4277
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View 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"

View File

@@ -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
}
}

View File

@@ -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(

View File

@@ -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": "",

View File

@@ -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 @@
}
}
}
}
}

View File

@@ -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(),

View File

@@ -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,

View File

@@ -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(),

View File

@@ -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
View 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));
}

View File

@@ -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',
}
})
},
},
],
}
])