mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-03-28 16:48:00 +03:00
Bridge: improvements
This commit is contained in:
139
src/Orion/bridge/index.ts
Normal file
139
src/Orion/bridge/index.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import type { ethers } from 'ethers';
|
||||
import {
|
||||
AtomicSwapStatus, type AtomicSwap, type SupportedChainId,
|
||||
type Unit, INTERNAL_PROTOCOL_PRECISION
|
||||
} from '../../index.js';
|
||||
import getHistoryExt from './getHistory.js';
|
||||
import swapExt from './swap.js';
|
||||
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
const backendStatusToAtomicSwapStatus = {
|
||||
LOCKED: AtomicSwapStatus.ROUTING,
|
||||
CLAIMED: AtomicSwapStatus.SETTLED,
|
||||
REFUNDED: AtomicSwapStatus.REFUNDED,
|
||||
REDEEMED: AtomicSwapStatus.SETTLED,
|
||||
'BEFORE-REDEEM': AtomicSwapStatus.ROUTING,
|
||||
} as const;
|
||||
|
||||
export default class Bridge {
|
||||
constructor(
|
||||
private readonly unitsArray: Unit[],
|
||||
private readonly env?: string,
|
||||
) {}
|
||||
|
||||
async getMergedHistory(
|
||||
externalStoredAtomicSwaps: AtomicSwap[],
|
||||
walletAddress: string,
|
||||
) {
|
||||
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,
|
||||
status: asStatus,
|
||||
claimed,
|
||||
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
|
||||
}
|
||||
|
||||
// Define status
|
||||
let historyStatus = backendStatusToAtomicSwapStatus[asStatus.source];
|
||||
if (asStatus.source === 'LOCKED') {
|
||||
const historySwap = bridgeHistory[secretHash];
|
||||
if (historySwap?.status.target === 'REDEEMED') {
|
||||
historyStatus = AtomicSwapStatus.SETTLED;
|
||||
}
|
||||
}
|
||||
if (claimed) historyStatus = AtomicSwapStatus.SETTLED;
|
||||
let status: AtomicSwapStatus | undefined;
|
||||
if (
|
||||
[AtomicSwapStatus.SETTLED, AtomicSwapStatus.REFUNDED].includes(
|
||||
historyStatus,
|
||||
)
|
||||
) {
|
||||
status = historyStatus;
|
||||
} else {
|
||||
status = localSwap !== undefined ? localSwap.status : historyStatus;
|
||||
}
|
||||
|
||||
// Define secret
|
||||
const secret = localSwap !== undefined ? localSwap.secret : '';
|
||||
|
||||
// Define environment
|
||||
const env = localSwap?.env;
|
||||
|
||||
return {
|
||||
liquidityMigrationTxHash: localSwap?.liquidityMigrationTxHash,
|
||||
sourceNetwork: sourceChainId,
|
||||
targetNetwork: targetChainId,
|
||||
amount: new BigNumber(amount)
|
||||
.multipliedBy(new BigNumber(10).pow(INTERNAL_PROTOCOL_PRECISION))
|
||||
.toString(),
|
||||
walletAddress: sender,
|
||||
secret,
|
||||
secretHash,
|
||||
lockTransactionHash: transactions?.lock,
|
||||
refundTransactionHash: transactions?.refund,
|
||||
status,
|
||||
asset: assetName,
|
||||
expiration:
|
||||
expiration?.lock ?? creationDate.getTime() + 60 * 60 * 24 * 4, // Or default 4 days
|
||||
creationDate: creationDate.getTime(),
|
||||
env,
|
||||
redeemOrder: atomicSwap.redeemOrder,
|
||||
};
|
||||
}).filter((swap) => swap.env === undefined || swap.env === this.env);
|
||||
}
|
||||
|
||||
getHistory(address: string, limit = 1000) {
|
||||
return getHistoryExt(this.unitsArray, address, limit);
|
||||
}
|
||||
|
||||
swap(
|
||||
assetName: string,
|
||||
amount: BigNumber.Value,
|
||||
sourceChain: SupportedChainId,
|
||||
targetChain: SupportedChainId,
|
||||
signer: ethers.Signer,
|
||||
options: {
|
||||
autoApprove?: boolean
|
||||
logger?: (message: string) => void
|
||||
withdrawToWallet?: boolean
|
||||
}
|
||||
) {
|
||||
return swapExt({
|
||||
amount,
|
||||
assetName,
|
||||
sourceChain,
|
||||
targetChain,
|
||||
signer,
|
||||
unitsArray: this.unitsArray,
|
||||
options,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,10 @@ import {
|
||||
import getNativeCryptocurrencyName from '../../utils/getNativeCryptocurrencyName.js';
|
||||
import { denormalizeNumber, generateSecret, normalizeNumber, toUpperCase } from '../../utils/index.js';
|
||||
import type { SupportedChainId } from '../../types.js';
|
||||
import type Orion from '../index.js';
|
||||
import type { z } from 'zod';
|
||||
import type { placeAtomicSwapSchema } from '../../services/Aggregator/schemas/index.js';
|
||||
import { simpleFetch } from 'simple-typed-fetch';
|
||||
import type { Unit } from '../../index.js';
|
||||
|
||||
type Params = {
|
||||
assetName: string
|
||||
@@ -25,7 +25,7 @@ type Params = {
|
||||
sourceChain: SupportedChainId
|
||||
targetChain: SupportedChainId
|
||||
signer: ethers.Signer
|
||||
orion: Orion
|
||||
unitsArray: Unit[]
|
||||
options?: {
|
||||
withdrawToWallet?: boolean // By default, the transfer amount remains in the exchange contract
|
||||
autoApprove?: boolean
|
||||
@@ -40,7 +40,7 @@ export default async function swap({
|
||||
targetChain,
|
||||
signer,
|
||||
options,
|
||||
orion
|
||||
unitsArray,
|
||||
}: Params) {
|
||||
const startProcessingTime = Date.now();
|
||||
if (amount === '') throw new Error('Amount can not be empty');
|
||||
@@ -50,8 +50,11 @@ export default async function swap({
|
||||
if (amountBN.isNaN()) throw new Error(`Amount '${amountBN.toString()}' is not a number`);
|
||||
if (amountBN.lte(0)) throw new Error(`Amount '${amountBN.toString()}' should be greater than 0`);
|
||||
|
||||
const sourceChainUnit = orion.getUnit(sourceChain);
|
||||
const targetChainUnit = orion.getUnit(targetChain);
|
||||
const sourceChainUnit = unitsArray.find(({ chainId }) => chainId === sourceChain);
|
||||
const targetChainUnit = unitsArray.find(({ chainId }) => chainId === targetChain);
|
||||
|
||||
if (sourceChainUnit === undefined) throw new Error(`Source chain '${sourceChain}' not found`);
|
||||
if (targetChainUnit === undefined) throw new Error(`Target chain '${targetChain}' not found`);
|
||||
|
||||
const {
|
||||
blockchainService: sourceBlockchainService,
|
||||
|
||||
@@ -1,36 +1,12 @@
|
||||
import type { BigNumber } from 'bignumber.js';
|
||||
import type { ethers } from 'ethers';
|
||||
import { merge } from 'merge-anything';
|
||||
import { chains, envs } from '../config/index.js';
|
||||
import type { networkCodes } from '../constants/index.js';
|
||||
import Unit from '../Unit/index.js';
|
||||
import { ReferralSystem } from '../services/ReferralSystem/index.js';
|
||||
import type { SupportedChainId, DeepPartial, VerboseUnitConfig, KnownEnv } from '../types.js';
|
||||
import type { SupportedChainId, DeepPartial, VerboseUnitConfig, KnownEnv, EnvConfig, AggregatedAssets } from '../types.js';
|
||||
import { isValidChainId } from '../utils/index.js';
|
||||
import swap from './bridge/swap.js';
|
||||
import getHistory from './bridge/getHistory.js';
|
||||
import { simpleFetch } from 'simple-typed-fetch';
|
||||
|
||||
type EnvConfig = {
|
||||
analyticsAPI: string
|
||||
referralAPI: string
|
||||
networks: Partial<
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseUnitConfig
|
||||
>
|
||||
>
|
||||
}
|
||||
type AggregatedAssets = Partial<
|
||||
Record<
|
||||
string,
|
||||
Partial<
|
||||
Record<SupportedChainId, {
|
||||
address: string
|
||||
}>
|
||||
>
|
||||
>
|
||||
>;
|
||||
import Bridge from './bridge/index.js';
|
||||
|
||||
export default class Orion {
|
||||
public readonly env?: string;
|
||||
@@ -39,6 +15,8 @@ export default class Orion {
|
||||
|
||||
public readonly referralSystem: ReferralSystem;
|
||||
|
||||
public readonly bridge: Bridge;
|
||||
|
||||
// TODO: get tradable assets (aggregated)
|
||||
|
||||
// TODO: get tradable pairs (aggregated)
|
||||
@@ -117,6 +95,11 @@ export default class Orion {
|
||||
[chainId]: unit,
|
||||
}
|
||||
}, {});
|
||||
|
||||
this.bridge = new Bridge(
|
||||
this.unitsArray,
|
||||
this.env,
|
||||
);
|
||||
}
|
||||
|
||||
get unitsArray() {
|
||||
@@ -211,28 +194,4 @@ export default class Orion {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bridge = {
|
||||
getHistory: (address: string, limit = 1000) => getHistory(this.unitsArray, address, limit),
|
||||
swap: (
|
||||
assetName: string,
|
||||
amount: BigNumber.Value,
|
||||
sourceChain: SupportedChainId,
|
||||
targetChain: SupportedChainId,
|
||||
signer: ethers.Signer,
|
||||
options: {
|
||||
autoApprove?: boolean
|
||||
logger?: (message: string) => void
|
||||
withdrawToWallet?: boolean
|
||||
}
|
||||
) => swap({
|
||||
amount,
|
||||
assetName,
|
||||
sourceChain,
|
||||
targetChain,
|
||||
signer,
|
||||
orion: this,
|
||||
options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
99
src/types.ts
99
src/types.ts
@@ -250,3 +250,102 @@ export type VerboseUnitConfig = {
|
||||
export type KnownEnv = typeof knownEnvs[number];
|
||||
|
||||
export type Json = string | number | boolean | null | Json[] | { [key: string]: Json };
|
||||
|
||||
export type EnvConfig = {
|
||||
analyticsAPI: string
|
||||
referralAPI: string
|
||||
networks: Partial<
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseUnitConfig
|
||||
>
|
||||
>
|
||||
}
|
||||
export type AggregatedAssets = Partial<
|
||||
Record<
|
||||
string,
|
||||
Partial<
|
||||
Record<SupportedChainId, {
|
||||
address: string
|
||||
}>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
||||
export type RedeemOrder = {
|
||||
sender: string
|
||||
receiver: string
|
||||
asset: string
|
||||
amount: number
|
||||
expiration: number
|
||||
secretHash: string
|
||||
signature: string
|
||||
claimReceiver: string
|
||||
}
|
||||
|
||||
export enum AtomicSwapStatus {
|
||||
ROUTING_REQUESTED = 'ROUTING_REQUESTED',
|
||||
ROUTING_PENDING = 'ROUTING_PENDING',
|
||||
ROUTING = 'ROUTING',
|
||||
ROUTING_FAILED = 'ROUTING_FAILED',
|
||||
|
||||
// ACCEPTED = 'ACCEPTED',
|
||||
FAILED = 'FAILED',
|
||||
REJECTED = 'REJECTED',
|
||||
|
||||
CHECK_REDEEM_THROUGH_OB_AVAILABLE = 'CHECK_REDEEM_THROUGH_OB_AVAILABLE',
|
||||
// Redeem
|
||||
// Blockchain redeem
|
||||
READY_TO_REDEEM = 'READY_TO_REDEEM',
|
||||
REDEEM_REQUESTED = 'REDEEM_REQUESTED',
|
||||
REDEEM_PENDING = 'REDEEM_PENDING',
|
||||
REDEEM_FAILED = 'REDEEM_FAILED',
|
||||
|
||||
// Orion blockchain redeem
|
||||
READY_TO_REDEEM_THROUGH_OB = 'READY_TO_REDEEM_THROUGH_OB',
|
||||
REDEEM_PENDING_THROUGH_OB = 'REDEEM_PENDING_THROUGH_OB',
|
||||
REDEEM_FAILED_THROUGH_OB = 'REDEEM_FAILED_THROUGH_OB',
|
||||
|
||||
SETTLED = 'SETTLED',
|
||||
|
||||
// Refund
|
||||
REFUND_REQUESTED = 'REFUND_REQUESTED',
|
||||
REFUND_PENDING = 'REFUND_PENDING',
|
||||
REFUNDED = 'REFUNDED',
|
||||
REFUND_FAILED = 'REFUND_FAILED',
|
||||
}
|
||||
|
||||
export type AtomicSwap = {
|
||||
secret: string
|
||||
secretHash: string
|
||||
status: AtomicSwapStatus
|
||||
|
||||
walletAddress: string
|
||||
env?: string | undefined
|
||||
|
||||
sourceNetwork?: SupportedChainId
|
||||
targetNetwork?: SupportedChainId
|
||||
|
||||
amount?: string
|
||||
asset?: string
|
||||
|
||||
creationDate?: number
|
||||
expiration?: number
|
||||
|
||||
lockTransactionHash?: string
|
||||
redeemTransactionHash?: string
|
||||
refundTransactionHash?: string
|
||||
liquidityMigrationTxHash?: string
|
||||
|
||||
redeemOrder?: RedeemOrder
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user