diff --git a/package.json b/package.json index 29de681..ecc901c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orionprotocol/sdk", - "version": "0.20.88-rc5", + "version": "0.20.88-rc6", "description": "Orion Protocol SDK", "main": "./lib/index.cjs", "module": "./lib/index.js", diff --git a/src/Unit/Exchange/getSwapInfo.ts b/src/Unit/Exchange/getSwapInfo.ts index aa1f5c5..33a2756 100644 --- a/src/Unit/Exchange/getSwapInfo.ts +++ b/src/Unit/Exchange/getSwapInfo.ts @@ -19,6 +19,7 @@ export type GetSwapInfoParams = { poolOnly?: boolean } walletAddress?: string + isExactReceive?: boolean } export default async function getSwapInfo({ @@ -30,6 +31,7 @@ export default async function getSwapInfo({ aggregator, options, walletAddress, + isExactReceive = false, }: GetSwapInfoParams) { if (amount === '') throw new Error('Amount can not be empty'); if (assetIn === '') throw new Error('AssetIn can not be empty'); @@ -66,6 +68,7 @@ export default async function getSwapInfo({ options?.poolOnly !== undefined && options.poolOnly ? 'pools' : undefined, + isExactReceive, ); const { exchanges: swapExchanges } = swapInfo; diff --git a/src/Unit/Exchange/swapLimit.ts b/src/Unit/Exchange/swapLimit.ts index a0ddd81..c914f1a 100644 --- a/src/Unit/Exchange/swapLimit.ts +++ b/src/Unit/Exchange/swapLimit.ts @@ -34,6 +34,7 @@ export type SwapLimitParams = { route?: 'aggregator' | 'pool' } } + isExactReceive?: boolean } type AggregatorOrder = { @@ -65,6 +66,7 @@ export default async function swapLimit({ signer, unit, options, + isExactReceive = false, }: SwapLimitParams): Promise { if (options?.developer) options.logger?.('YOU SPECIFIED A DEVELOPER OPTIONS. BE CAREFUL!'); if (amount === '') throw new Error('Amount can not be empty'); @@ -143,6 +145,7 @@ export default async function swapLimit({ options?.poolOnly !== undefined && options.poolOnly ? 'pools' : undefined, + isExactReceive, ); const { exchanges: swapExchanges, exchangeContractPath } = swapInfo; @@ -151,7 +154,11 @@ export default async function swapLimit({ if (swapExchanges.length > 0) options?.logger?.(`Swap exchanges: ${swapExchanges.join(', ')}`); - if (amountBN.lt(swapInfo.minAmountIn)) { + if (swapInfo?.isExactReceive && amountBN.lt(swapInfo.minAmountOut)) { + throw new Error(`Amount is too low. Min amountOut is ${swapInfo.minAmountOut} ${assetOut}`); + } + + if (!(swapInfo?.isExactReceive) && amountBN.lt(swapInfo.minAmountIn)) { throw new Error(`Amount is too low. Min amountIn is ${swapInfo.minAmountIn} ${assetIn}`); } @@ -193,7 +200,9 @@ export default async function swapLimit({ options?.logger?.(`Safe price is ${swapInfo.orderInfo.safePrice} ${quoteAssetName}`); // BTEMP — better than or equal market price - const priceIsBTEMP = priceBN.lte(swapInfo.orderInfo.safePrice); + const priceIsBTEMP = isExactReceive + ? priceBN.gte(swapInfo.orderInfo.safePrice) + : priceBN.lte(swapInfo.orderInfo.safePrice); options?.logger?.(`Your price ${priceBN.toString()} is ${priceIsBTEMP ? 'better than or equal' : 'worse than'} market price ${swapInfo.orderInfo.safePrice}`); @@ -237,7 +246,9 @@ export default async function swapLimit({ if (factoryAddress !== undefined) options?.logger?.(`Factory address is ${factoryAddress}. Exchange is ${firstSwapExchange}`); } - const amountSpend = swapInfo.amountIn; + const amountSpend = !(swapInfo?.isExactReceive) + ? swapInfo.amountIn + : new BigNumber(swapInfo.orderInfo.amount).multipliedBy(swapInfo.orderInfo.safePrice) balanceGuard.registerRequirement({ reason: 'Amount spend', @@ -250,7 +261,9 @@ export default async function swapLimit({ sources: getAvailableSources('amount', assetInAddress, 'pool'), }); - const amountReceive = new BigNumber(swapInfo.orderInfo.amount).multipliedBy(swapInfo.orderInfo.safePrice) + const amountReceive = swapInfo?.isExactReceive + ? swapInfo.amountOut + : new BigNumber(swapInfo.orderInfo.amount).multipliedBy(swapInfo.orderInfo.safePrice) const amountSpendBlockchainParam = normalizeNumber( amountSpend, INTERNAL_PROTOCOL_PRECISION, diff --git a/src/Unit/Exchange/swapMarket.ts b/src/Unit/Exchange/swapMarket.ts index 21bf775..4253bc7 100644 --- a/src/Unit/Exchange/swapMarket.ts +++ b/src/Unit/Exchange/swapMarket.ts @@ -37,7 +37,6 @@ type PoolSwap = { export type Swap = AggregatorOrder | PoolSwap; - const isValidSingleSwap = (singleSwap: Omit & { factory: string }): singleSwap is SingleSwap => { return isValidFactory(singleSwap.factory); } @@ -51,6 +50,7 @@ export default async function swapMarket({ signer, unit, options, + isExactReceive = false, }: SwapMarketParams): Promise { if (options?.developer) options.logger?.('YOU SPECIFIED A DEVELOPER OPTIONS. BE CAREFUL!'); @@ -131,6 +131,7 @@ export default async function swapMarket({ options?.poolOnly !== undefined && options.poolOnly ? 'pools' : undefined, + isExactReceive, ); const { exchanges: swapExchanges, exchangeContractPath } = swapInfo; @@ -139,7 +140,11 @@ export default async function swapMarket({ if (swapExchanges.length > 0) options?.logger?.(`Swap exchanges: ${swapExchanges.join(', ')}`); - if (amountBN.lt(swapInfo.minAmountIn)) { + if (swapInfo?.isExactReceive && amountBN.lt(swapInfo.minAmountOut)) { + throw new Error(`Amount is too low. Min amountOut is ${swapInfo.minAmountOut} ${assetOut}`); + } + + if (!(swapInfo?.isExactReceive) && amountBN.lt(swapInfo.minAmountIn)) { throw new Error(`Amount is too low. Min amountIn is ${swapInfo.minAmountIn} ${assetIn}`); } @@ -193,8 +198,11 @@ export default async function swapMarket({ const amountOutWithSlippage = new BigNumber(swapInfo.amountOut) .multipliedBy(new BigNumber(1).minus(percent)) .toString(); + const amountInWithSlippage = new BigNumber(swapInfo.amountIn) + .multipliedBy(new BigNumber(1).plus(percent)) + .toString(); - const amountSpend = swapInfo.amountIn; + const amountSpend = swapInfo?.isExactReceive ? amountInWithSlippage : swapInfo.amountIn; balanceGuard.registerRequirement({ reason: 'Amount spend', @@ -207,13 +215,14 @@ export default async function swapMarket({ sources: getAvailableSources('amount', assetInAddress, 'pool'), }); + const amountReceive = swapInfo?.isExactReceive ? amountOutWithSlippage : swapInfo.amountOut; const amountSpendBlockchainParam = normalizeNumber( amountSpend, INTERNAL_PROTOCOL_PRECISION, BigNumber.ROUND_CEIL, ); const amountReceiveBlockchainParam = normalizeNumber( - amountOutWithSlippage, + amountReceive, INTERNAL_PROTOCOL_PRECISION, BigNumber.ROUND_FLOOR, ); diff --git a/src/services/Aggregator/index.ts b/src/services/Aggregator/index.ts index 93869f9..8ed790f 100644 --- a/src/services/Aggregator/index.ts +++ b/src/services/Aggregator/index.ts @@ -268,11 +268,16 @@ class Aggregator { amount: string, instantSettlement?: boolean, exchanges?: string[] | 'cex' | 'pools', + isExactReceive?: boolean, ) => { const url = new URL(`${this.apiUrl}/api/v1/swap`); url.searchParams.append('assetIn', assetIn); url.searchParams.append('assetOut', assetOut); - url.searchParams.append('amountIn', amount); + if (isExactReceive !== true) { + url.searchParams.append('amountIn', amount); + } else { + url.searchParams.append('amountOut', amount); + } if (exchanges !== undefined) { if (Array.isArray(exchanges)) { diff --git a/src/services/Aggregator/schemas/swapInfoSchema.ts b/src/services/Aggregator/schemas/swapInfoSchema.ts index ad1219d..59de289 100644 --- a/src/services/Aggregator/schemas/swapInfoSchema.ts +++ b/src/services/Aggregator/schemas/swapInfoSchema.ts @@ -52,11 +52,26 @@ const swapInfoBase = z.object({ autoSlippage: z.number().optional(), }); -const swapInfoSchema = swapInfoBase.extend({ +const swapInfoByAmountIn = swapInfoBase.extend({ availableAmountOut: z.null(), availableAmountIn: z.number(), marketAmountOut: z.number().nullable(), marketAmountIn: z.null(), -}); +}).transform((val) => ({ + ...val, + isExactReceive: false as const, +})); + +const swapInfoByAmountOut = swapInfoBase.extend({ + availableAmountOut: z.number(), + availableAmountIn: z.null(), + marketAmountOut: z.null(), + marketAmountIn: z.number().nullable(), +}).transform((val) => ({ + ...val, + isExactReceive: true as const, +})); + +const swapInfoSchema = swapInfoByAmountIn.or(swapInfoByAmountOut); export default swapInfoSchema; diff --git a/src/services/Aggregator/ws/index.ts b/src/services/Aggregator/ws/index.ts index 34a19a5..d654e22 100644 --- a/src/services/Aggregator/ws/index.ts +++ b/src/services/Aggregator/ws/index.ts @@ -544,11 +544,27 @@ class AggregatorWS { autoSlippage: json.sl, }; - this.subscriptions[SubscriptionType.SWAP_SUBSCRIBE]?.[json.S]?.callback({ - marketAmountOut: json.mo, - availableAmountIn: json.aa, - ...baseSwapInfo, - }); + switch (json.er) { // exactReceive + case false: + this.subscriptions[SubscriptionType.SWAP_SUBSCRIBE]?.[json.S]?.callback({ + isExactReceive: false, + marketAmountOut: json.mo, + availableAmountIn: json.aa, + ...baseSwapInfo, + }); + + break; + case true: + this.subscriptions[SubscriptionType.SWAP_SUBSCRIBE]?.[json.S]?.callback({ + isExactReceive: true, + ...baseSwapInfo, + marketAmountIn: json.mi, + availableAmountOut: json.aao, + }); + break; + default: + break; + } } break; case MessageType.INITIALIZATION: diff --git a/src/services/Aggregator/ws/schemas/swapInfoSchema.ts b/src/services/Aggregator/ws/schemas/swapInfoSchema.ts index 38eeae9..828f6a1 100644 --- a/src/services/Aggregator/ws/schemas/swapInfoSchema.ts +++ b/src/services/Aggregator/ws/schemas/swapInfoSchema.ts @@ -51,9 +51,25 @@ const swapInfoSchemaBase = baseMessageSchema.extend({ sl: z.number().optional(), }); -const swapInfoSchema = swapInfoSchemaBase.extend({ +const swapInfoSchemaByAmountIn = swapInfoSchemaBase.extend({ mo: z.number().optional(), // market amount out aa: z.number(), // available amount in -}); +}).transform((content) => ({ + ...content, + er: false as const, +})); + +const swapInfoSchemaByAmountOut = swapInfoSchemaBase.extend({ + mi: z.number().optional(), // market amount in + aao: z.number(), // available amount out +}).transform((content) => ({ + ...content, + er: true as const, +})); + +const swapInfoSchema = z.union([ + swapInfoSchemaByAmountIn, + swapInfoSchemaByAmountOut, +]); export default swapInfoSchema; diff --git a/src/types.ts b/src/types.ts index 946675a..1a03fcd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -215,11 +215,20 @@ export type SwapInfoBase = { autoSlippage: number | undefined } -export type SwapInfo = SwapInfoBase & { +export type SwapInfoByAmountIn = SwapInfoBase & { + isExactReceive: false availableAmountIn?: number | undefined marketAmountOut?: number | undefined } +export type SwapInfoByAmountOut = SwapInfoBase & { + isExactReceive: true + marketAmountIn?: number | undefined + availableAmountOut?: number | undefined +} + +export type SwapInfo = SwapInfoByAmountIn | SwapInfoByAmountOut; + export enum HistoryTransactionStatus { PENDING = 'Pending', DONE = 'Done',