mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-04-03 11:38:09 +03:00
Added combineLocalAndExternalData to bridge
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@orionprotocol/sdk",
|
||||
"version": "0.19.59",
|
||||
"version": "0.19.60",
|
||||
"description": "Orion Protocol SDK",
|
||||
"main": "./lib/index.cjs",
|
||||
"module": "./lib/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
|
||||
target?: TargetItem['state'] | undefined
|
||||
aggregator?: AggItem['status'] | undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,43 @@
|
||||
import { ethers } from 'ethers';
|
||||
import {
|
||||
type AtomicSwap, type SupportedChainId,
|
||||
type Unit, INTERNAL_PROTOCOL_PRECISION
|
||||
import type {
|
||||
Orion, Unit, AtomicSwapLocal, SupportedChainId, TransactionInfo
|
||||
} from '../../index.js';
|
||||
import { INTERNAL_PROTOCOL_PRECISION, TxStatus, TxType } from '../../index.js';
|
||||
import getHistoryExt 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';
|
||||
|
||||
export const SECONDS_IN_DAY = 60 * 60 * 24;
|
||||
export const EXPIRATION_DAYS = 4;
|
||||
|
||||
type BridgeHistory = Awaited<ReturnType<Orion['bridge']['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
|
||||
}
|
||||
export default class Bridge {
|
||||
constructor(
|
||||
private readonly unitsArray: Unit[],
|
||||
@@ -74,6 +101,145 @@ export default class Bridge {
|
||||
});
|
||||
}
|
||||
|
||||
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 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 assetName = 'asdf';
|
||||
|
||||
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,
|
||||
|
||||
79
src/types.ts
79
src/types.ts
@@ -283,36 +283,73 @@ 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 {
|
||||
PENDING = 'pending',
|
||||
FAILED = 'failed',
|
||||
SETTLED = 'settled',
|
||||
}
|
||||
|
||||
export enum TxType {
|
||||
BRIDGE_LOCK = 'BRIDGE_LOCK',
|
||||
BRIDGE_REDEEM = 'BRIDGE_REDEEM',
|
||||
BRIDGE_REFUND = 'BRIDGE_REFUND',
|
||||
}
|
||||
|
||||
type BridgeRedeemTxPayload = {
|
||||
type: TxType.BRIDGE_REDEEM
|
||||
data: {
|
||||
secretHash: string
|
||||
}
|
||||
}
|
||||
|
||||
type BridgeLockTxPayload = {
|
||||
type: TxType.BRIDGE_LOCK
|
||||
data: {
|
||||
secretHash: string
|
||||
}
|
||||
}
|
||||
|
||||
type BridgeRefundTxPayload = {
|
||||
type: TxType.BRIDGE_REFUND
|
||||
data: {
|
||||
secretHash: string
|
||||
}
|
||||
}
|
||||
|
||||
export type TransactionInfo = {
|
||||
id?: string
|
||||
status?: TxStatus
|
||||
hash?: string
|
||||
payload?: BridgeLockTxPayload | BridgeRedeemTxPayload | BridgeRefundTxPayload
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
Reference in New Issue
Block a user