From dec7104a5167c4731b981e6c86911152a2d9c1dc Mon Sep 17 00:00:00 2001 From: Anton-rock Date: Fri, 13 Sep 2024 16:06:39 +0300 Subject: [PATCH 1/2] feat: fix market orders --- package.json | 2 +- pnpm-lock.yaml | 10 +- .../OrderbookAndTradesInterface.tsx | 39 ++++---- .../SpotOrderBook/SpotOrderBook.tsx | 92 ++++++++++++------- .../RightBlock/CreateOrder/CreateOrder.tsx | 1 + .../RightBlock/CreateOrder/CreateOrderVM.tsx | 75 ++++++++++++--- src/screens/SpotScreen/SpotScreenMobile.tsx | 5 +- src/screens/SwapScreen/SwapScreen.tsx | 16 +--- src/stores/RootStore.ts | 3 + .../SpotOrderBookStore.ts} | 21 ++--- src/stores/SwapStore.ts | 53 +++++++++-- src/stores/index.ts | 2 + 12 files changed, 204 insertions(+), 115 deletions(-) rename src/{screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderbookVM.tsx => stores/SpotOrderBookStore.ts} (91%) diff --git a/package.json b/package.json index 534768a8..767b5c65 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "pnpm": ">=9.7.0" }, "dependencies": { - "@compolabs/spark-orderbook-ts-sdk": "^1.8.2", + "@compolabs/spark-orderbook-ts-sdk": "^1.8.4", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@fuels/connectors": "^0.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 349361c1..11e56451 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@compolabs/spark-orderbook-ts-sdk': - specifier: ^1.8.2 - version: 1.8.2(@types/react@18.3.4)(fuels@0.93.0)(graphql@16.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.8.4 + version: 1.8.4(@types/react@18.3.4)(fuels@0.93.0)(graphql@16.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@emotion/react': specifier: ^11.11.3 version: 11.13.3(@types/react@18.3.4)(react@18.3.1) @@ -982,8 +982,8 @@ packages: '@coinbase/wallet-sdk@4.0.4': resolution: {integrity: sha512-74c040CRnGhfRjr3ArnkAgud86erIqdkPHNt5HR1k9u97uTIZCJww9eGYT67Qf7gHPpGS/xW8Be1D4dvRm63FA==} - '@compolabs/spark-orderbook-ts-sdk@1.8.2': - resolution: {integrity: sha512-i3MKp0ORK8P5lscsqzQ9Wl0yC+y2yB9lGmjD2N7/2guzIq+8KUhjSijdTIZ65SFX7v5ms7ssqFmPnxJOiMQFkA==} + '@compolabs/spark-orderbook-ts-sdk@1.8.4': + resolution: {integrity: sha512-9YlOQf0G3ikyzxKadZ/ZuuC3WYLOmBFGsPtrLYjnxKBDm06xzJ0GTZp2xJhm02dko2GuweQbEJW5dm7XOOrPHA==} engines: {node: '>=18'} peerDependencies: fuels: '>=0.93.0' @@ -8247,7 +8247,7 @@ snapshots: preact: 10.23.2 sha.js: 2.4.11 - '@compolabs/spark-orderbook-ts-sdk@1.8.2(@types/react@18.3.4)(fuels@0.93.0)(graphql@16.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@compolabs/spark-orderbook-ts-sdk@1.8.4(@types/react@18.3.4)(fuels@0.93.0)(graphql@16.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@apollo/client': 3.11.8(@types/react@18.3.4)(graphql-ws@5.16.0(graphql@16.9.0))(graphql@16.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) bignumber.js: 9.1.2 diff --git a/src/screens/SpotScreen/OrderbookAndTradesInterface/OrderbookAndTradesInterface.tsx b/src/screens/SpotScreen/OrderbookAndTradesInterface/OrderbookAndTradesInterface.tsx index c637c242..c7d97a64 100644 --- a/src/screens/SpotScreen/OrderbookAndTradesInterface/OrderbookAndTradesInterface.tsx +++ b/src/screens/SpotScreen/OrderbookAndTradesInterface/OrderbookAndTradesInterface.tsx @@ -6,7 +6,6 @@ import SizedBox from "@components/SizedBox"; import Text, { TEXT_TYPES } from "@src/components/Text"; import { SpotOrderBook } from "./SpotOrderBook/SpotOrderBook"; -import { SpotOrderbookVMProvider } from "./SpotOrderBook/SpotOrderbookVM"; import { SpotTrades } from "./SpotTrades/SpotTrades"; import { SpotTradesVMProvider } from "./SpotTrades/SpotTradesVM"; @@ -14,26 +13,24 @@ const OrderbookAndTradesInterface: React.FC = () => { const [isOrderbook, setIsOrderbook] = useState(true); return ( - - - - - - - - - {isOrderbook ? : } - - - + + + + + + + + {isOrderbook ? : } + + ); }; diff --git a/src/screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderBook.tsx b/src/screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderBook.tsx index 81aa3f53..491a7511 100644 --- a/src/screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderBook.tsx +++ b/src/screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderBook.tsx @@ -24,8 +24,6 @@ import { useStores } from "@stores"; import { ORDER_MODE, useCreateOrderVM } from "../../RightBlock/CreateOrder/CreateOrderVM"; -import { useSpotOrderbookVM } from "./SpotOrderbookVM"; - interface IProps extends HTMLAttributes {} export enum SPOT_ORDER_FILTER { @@ -43,7 +41,7 @@ const SPOT_SETTINGS_ICONS = { }; export const SpotOrderBook: React.FC = observer(() => { - const vm = useSpotOrderbookVM(); + const { spotOrderBookStore } = useStores(); const orderSpotVm = useCreateOrderVM(); const media = useMedia(); const theme = useTheme(); @@ -53,18 +51,19 @@ export const SpotOrderBook: React.FC = observer(() => { const [isSettingsOpen, openSettings, closeSettings] = useFlag(); useEffect(() => { - vm.calcSize(media.mobile); + spotOrderBookStore.calcSize(media.mobile); }, [media.mobile]); const handleCalcSize = useCallback(() => { - vm.calcSize(media.mobile); + spotOrderBookStore.calcSize(media.mobile); }, [media.mobile]); useEventListener("resize", handleCalcSize); - const isOrderBookEmpty = vm.allBuyOrders.length === 0 && vm.allSellOrders.length === 0; + const isOrderBookEmpty = + spotOrderBookStore.allBuyOrders.length === 0 && spotOrderBookStore.allSellOrders.length === 0; - if (vm.isOrderBookLoading && isOrderBookEmpty) { + if (spotOrderBookStore.isOrderBookLoading && isOrderBookEmpty) { return ; } @@ -85,9 +84,9 @@ export const SpotOrderBook: React.FC = observer(() => { vm.setOrderFilter(index)} + onClick={() => spotOrderBookStore.setOrderFilter(index)} /> )); }; @@ -97,9 +96,9 @@ export const SpotOrderBook: React.FC = observer(() => { return ( - {vm.spreadPrice} + {spotOrderBookStore.spreadPrice} - {`(${vm.spreadPercent}%)`} + {`(${spotOrderBookStore.spreadPercent}%)`} ); } @@ -107,31 +106,35 @@ export const SpotOrderBook: React.FC = observer(() => { return ( SPREAD - {vm.spreadPrice} - {`(${vm.spreadPercent}%) `} + {spotOrderBookStore.spreadPrice} + {`(${spotOrderBookStore.spreadPercent}%) `} ); }; - const indexOfDecimal = SPOT_DECIMAL_OPTIONS.indexOf(vm.decimalGroup); + const indexOfDecimal = SPOT_DECIMAL_OPTIONS.indexOf(spotOrderBookStore.decimalGroup); const handleDecimalSelect = (index: string) => { const value = SPOT_DECIMAL_OPTIONS[Number(index)]; - vm.setDecimalGroup(value); + spotOrderBookStore.setDecimalGroup(value); }; const renderOrders = (orders: SpotMarketOrder[], type: "sell" | "buy") => { const orderMode = type === "sell" ? ORDER_MODE.BUY : ORDER_MODE.SELL; const volumePercent = (ord: SpotMarketOrder) => - type === "sell" ? ord.initialAmount.div(vm.totalSell) : ord.initialQuoteAmount.div(vm.totalBuy); + type === "sell" + ? ord.initialAmount.div(spotOrderBookStore.totalSell) + : ord.initialQuoteAmount.div(spotOrderBookStore.totalBuy); const color = type === "sell" ? theme.colors.redLight : theme.colors.greenLight; return orders.map((o, index) => ( orderSpotVm.selectOrderbookOrder(o, orderMode)}> {o.currentAmountUnits.toFormat(4)} - {o.priceUnits.toFormat(vm.decimalGroup)} - {numeral(o.currentQuoteAmountUnits).format(`0.${"0".repeat(vm.decimalGroup)}a`)} + {o.priceUnits.toFormat(spotOrderBookStore.decimalGroup)} + + {numeral(o.currentQuoteAmountUnits).format(`0.${"0".repeat(spotOrderBookStore.decimalGroup)}a`)} + )); }; @@ -156,33 +159,56 @@ export const SpotOrderBook: React.FC = observer(() => { {`Total ${market?.quoteToken.symbol}`} - {vm.orderFilter === SPOT_ORDER_FILTER.SELL_AND_BUY && ( + {spotOrderBookStore.orderFilter === SPOT_ORDER_FILTER.SELL_AND_BUY && ( )} - {vm.orderFilter === SPOT_ORDER_FILTER.SELL && ( + {spotOrderBookStore.orderFilter === SPOT_ORDER_FILTER.SELL && ( )} - {vm.orderFilter !== SPOT_ORDER_FILTER.BUY && renderOrders(vm.sellOrders, "sell")} + {spotOrderBookStore.orderFilter !== SPOT_ORDER_FILTER.BUY && + renderOrders(spotOrderBookStore.sellOrders, "sell")} - {vm.orderFilter === SPOT_ORDER_FILTER.SELL_AND_BUY && renderSpread()} + {spotOrderBookStore.orderFilter === SPOT_ORDER_FILTER.SELL_AND_BUY && renderSpread()} - {vm.orderFilter !== SPOT_ORDER_FILTER.SELL && renderOrders(vm.buyOrders, "buy")} + {spotOrderBookStore.orderFilter !== SPOT_ORDER_FILTER.SELL && + renderOrders(spotOrderBookStore.buyOrders, "buy")} - {vm.orderFilter === SPOT_ORDER_FILTER.BUY && ( + {spotOrderBookStore.orderFilter === SPOT_ORDER_FILTER.BUY && ( )} - {vm.orderFilter === SPOT_ORDER_FILTER.SELL_AND_BUY && ( - + {spotOrderBookStore.orderFilter === SPOT_ORDER_FILTER.SELL_AND_BUY && ( + )} @@ -191,10 +217,10 @@ export const SpotOrderBook: React.FC = observer(() => { filterIcons={Object.entries(SPOT_SETTINGS_ICONS).map(([key, value]) => value)} isOpen={isSettingsOpen} selectedDecimal={String(indexOfDecimal)} - selectedFilter={vm.orderFilter} + selectedFilter={spotOrderBookStore.orderFilter} onClose={closeSettings} onDecimalSelect={handleDecimalSelect} - onFilterSelect={vm.setOrderFilter} + onFilterSelect={spotOrderBookStore.setOrderFilter} /> diff --git a/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrder.tsx b/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrder.tsx index b2b9fac4..2bcf8ef2 100644 --- a/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrder.tsx +++ b/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrder.tsx @@ -126,6 +126,7 @@ const CreateOrder: React.FC = observer(() => { }; const renderInstruction = () => { + if (settingsStore.orderType === ORDER_TYPE.Market) return <>; const handleChangeTimeInForce = (e: any) => { settingsStore.setTimeInForce(e); }; diff --git a/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrderVM.tsx b/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrderVM.tsx index 7d435140..8fa5c241 100644 --- a/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrderVM.tsx +++ b/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrderVM.tsx @@ -89,17 +89,18 @@ class CreateOrderVM { () => oracleStore.prices, () => { const { orderType } = settingsStore; - const token = tradeStore.market?.baseToken; - const price = token?.priceFeed ? oracleStore.getTokenIndexPrice(token?.priceFeed) : BN.ZERO; - + const { spotOrderBookStore } = this.rootStore; + const order = this.isSell + ? spotOrderBookStore.buyOrders[0] + : spotOrderBookStore.sellOrders[spotOrderBookStore.sellOrders.length - 1]; if (orderType === ORDER_TYPE.Market) { - this.setInputPriceThrottle(price); + this.setInputPriceThrottle(order.price); } else if ( orderType === ORDER_TYPE.Limit && this.inputPrice.isZero() && this.activeInput !== ACTIVE_INPUT.Price ) { - this.setInputPriceThrottle(price); + this.setInputPriceThrottle(order.price); } }, ); @@ -314,6 +315,7 @@ class CreateOrderVM { ) .map((m) => m.contractId); + console.log("marketContracts", marketContracts); if (bcNetwork.getIsExternalWallet()) { notificationStore.info({ text: "Please, confirm operation in your wallet" }); } @@ -362,6 +364,7 @@ class CreateOrderVM { price: this.inputPrice.toString(), ...deposit, }; + console.log("order", order); console.log(order); @@ -383,27 +386,71 @@ class CreateOrderVM { asset: market.baseToken.assetId ?? "", status: ["Active"], }; + const isBuy = type === OrderType.Buy; - const oppositeOrderType = type === OrderType.Buy ? OrderType.Sell : OrderType.Buy; + const oppositeOrderType = isBuy ? OrderType.Sell : OrderType.Buy; const orders = await bcNetwork.fetchSpotOrders({ ...params, orderType: oppositeOrderType }); - + let total = this.inputTotal; + let spend = BN.ZERO; + const arr = orders + .map((el) => { + if (total.toNumber() < 0) { + return null; + } + spend = spend.plus(el.currentAmount); + total = total.minus(el.currentQuoteAmount); + return el; + }) + .filter((el) => el !== null); + console.log("arr", arr); const price = - settingsStore.orderType === ORDER_TYPE.Market - ? orders[orders.length - 1].price.toString() - : this.inputPrice.toString(); + settingsStore.orderType === ORDER_TYPE.Market ? arr[arr.length - 1].price.toString() : this.inputPrice.toString(); + console.log("this.inputAmount", this.inputAmount.toString()); + console.log("price", price.toString()); + console.log("spend", spend.toString()); + // 72275.54 + deposit = { + ...deposit, + amountToSpend: this.inputAmount.toString(), + }; + + // { + // "amount": "13835", + // "orderType": "Sell", + // "limitType": "FOK", + // "price": "72275538014795", + // "orders": [ + // "0x6d96fca99833848826e6d95786a4e513bb5db72c6d4fc9d554b6464fc1ef97bd" + // ], + // "slippage": "10000", + // "amountToSpend": "13835", + // "amountFee": "16000", + // "depositAssetId": "0x38e4ca985b22625fff93205e997bfc5cc8453a953da638ad297ca60a9f2600bc", + // "feeAssetId": "0x336b7c06352a4b736ff6f688ba6885788b3df16e136e95310ade51aa32dc6f05", + // "assetType": "Base" + // } + + // { + // "type": "Sell", + // "amount": "13835", + // "price": "72275540000000", + // "amountToSpend": "13835", + // "amountFee": "16000", + // "depositAssetId": "0x38e4ca985b22625fff93205e997bfc5cc8453a953da638ad297ca60a9f2600bc", + // "feeAssetId": "0x336b7c06352a4b736ff6f688ba6885788b3df16e136e95310ade51aa32dc6f05", + // "assetType": "Base" + // } const order: FulfillOrderManyWithDepositParams = { amount: this.inputAmount.toString(), orderType: type, limitType: settingsStore.timeInForce, price, - orders: orders.map((el) => el.id), + orders: arr.map((el) => el.id), slippage: "10000", ...deposit, }; - - console.log(order); - + console.log("order", order); const data = await bcNetwork.fulfillOrderManyWithDeposit(order, marketContracts); return data.transactionId; }; diff --git a/src/screens/SpotScreen/SpotScreenMobile.tsx b/src/screens/SpotScreen/SpotScreenMobile.tsx index 02c6ad12..0e2d595b 100644 --- a/src/screens/SpotScreen/SpotScreenMobile.tsx +++ b/src/screens/SpotScreen/SpotScreenMobile.tsx @@ -12,7 +12,6 @@ import { media } from "@src/themes/breakpoints"; import { useStores } from "@stores"; import { SpotOrderBook } from "./OrderbookAndTradesInterface/SpotOrderBook/SpotOrderBook"; -import { SpotOrderbookVMProvider } from "./OrderbookAndTradesInterface/SpotOrderBook/SpotOrderbookVM"; import CreateOrder from "./RightBlock/CreateOrder"; import MarketSelection from "./RightBlock/MarketSelection"; import MarketStatistics from "./MarketStatistics"; @@ -38,9 +37,7 @@ const SpotScreenMobile: React.FC = observer(() => { return ( - - - + diff --git a/src/screens/SwapScreen/SwapScreen.tsx b/src/screens/SwapScreen/SwapScreen.tsx index ad24a75d..919931f9 100644 --- a/src/screens/SwapScreen/SwapScreen.tsx +++ b/src/screens/SwapScreen/SwapScreen.tsx @@ -30,7 +30,7 @@ export const SwapScreen: React.FC = observer(() => { const { isConnected } = useWallet(); const theme = useTheme(); const media = useMedia(); - const { swapStore, balanceStore, quickAssetsStore, tradeStore } = useStores(); + const { swapStore, balanceStore, tradeStore } = useStores(); const [isConnectDialogVisible, openConnectDialog, closeConnectDialog] = useFlag(); const bcNetwork = FuelNetwork.getInstance(); const [slippage, setSlippage] = useState(INITIAL_SLIPPAGE); @@ -76,14 +76,6 @@ export const SwapScreen: React.FC = observer(() => { return d.length > 0 ? d.filter((el) => assets.some((item) => item.assetId === el.assetId)) : []; }; - const getMarketPair = (baseAsset: Token, queryAsset: Token) => { - return markets.find( - (el) => - (el.baseToken.assetId === baseAsset.assetId && el.quoteToken.assetId === queryAsset.assetId) || - (el.baseToken.assetId === queryAsset.assetId && el.quoteToken.assetId === baseAsset.assetId), - ); - }; - const onPayAmountChange = (e: React.ChangeEvent) => { setOnPress(false); const newPayAmount = replaceComma(e.target.value); @@ -155,7 +147,7 @@ export const SwapScreen: React.FC = observer(() => { swapStore.setBuyToken(type === "sell" ? paris[0] : pair); swapStore.setPayAmount("0"); swapStore.setReceiveAmount("0"); - const marketId = getMarketPair(pair, paris[0]); + const marketId = swapStore.getMarketPair(pair, paris[0]); if (!marketId) return; tradeStore.selectActiveMarket(marketId.symbol); balanceStore.update(); @@ -183,7 +175,7 @@ export const SwapScreen: React.FC = observer(() => { { handleChangeMarketId(tokens, item, "sell"); @@ -217,7 +209,7 @@ export const SwapScreen: React.FC = observer(() => { { handleChangeMarketId(buyTokenOptions, item, "buy"); diff --git a/src/stores/RootStore.ts b/src/stores/RootStore.ts index 2c69e9da..ecc1bd29 100644 --- a/src/stores/RootStore.ts +++ b/src/stores/RootStore.ts @@ -13,6 +13,7 @@ import { BalanceStore } from "./BalanceStore"; import { ModalStore } from "./ModalStore"; import OracleStore from "./OracleStore"; import SwapStore from "./SwapStore"; +import SpotOrderBookStore from "./SpotOrderBookStore"; export interface ISerializedRootStore { accountStore?: ISerializedAccountStore; @@ -33,6 +34,7 @@ export default class RootStore { swapStore: SwapStore; mixPanelStore: MixPanelStore; quickAssetsStore: QuickAssetsStore; + spotOrderBookStore: SpotOrderBookStore; private constructor(initState?: ISerializedRootStore) { this.notificationStore = new NotificationStore(this); @@ -46,6 +48,7 @@ export default class RootStore { this.swapStore = new SwapStore(this); this.mixPanelStore = new MixPanelStore(this); this.quickAssetsStore = new QuickAssetsStore(this); + this.spotOrderBookStore = new SpotOrderBookStore(this); makeAutoObservable(this); diff --git a/src/screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderbookVM.tsx b/src/stores/SpotOrderBookStore.ts similarity index 91% rename from src/screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderbookVM.tsx rename to src/stores/SpotOrderBookStore.ts index 3178ff6c..60e6950e 100644 --- a/src/screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderbookVM.tsx +++ b/src/stores/SpotOrderBookStore.ts @@ -1,4 +1,4 @@ -import React, { useMemo } from "react"; +import React from "react"; import { GetActiveOrdersParams, OrderType } from "@compolabs/spark-orderbook-ts-sdk"; import { makeAutoObservable, reaction } from "mobx"; import { Nullable } from "tsdef"; @@ -6,30 +6,19 @@ import { Nullable } from "tsdef"; import { FuelNetwork } from "@src/blockchain"; import { DEFAULT_DECIMALS } from "@src/constants"; import { SpotMarketOrder } from "@src/entity"; -import useVM from "@src/hooks/useVM"; import { Subscription } from "@src/typings/utils"; import BN from "@src/utils/BN"; import { formatSpotMarketOrders } from "@src/utils/formatSpotMarketOrders"; import { groupOrders } from "@src/utils/groupOrders"; -import { RootStore, useStores } from "@stores"; +import { RootStore } from "@stores"; -import { SPOT_ORDER_FILTER } from "./SpotOrderBook"; - -const ctx = React.createContext(null); +import { SPOT_ORDER_FILTER } from "@src/screens/SpotScreen/OrderbookAndTradesInterface/SpotOrderBook/SpotOrderBook"; interface IProps { children: React.ReactNode; } -export const SpotOrderbookVMProvider: React.FC = ({ children }) => { - const rootStore = useStores(); - const store = useMemo(() => new SpotOrderbookVM(rootStore), [rootStore]); - return {children}; -}; - -export const useSpotOrderbookVM = () => useVM(ctx); - -class SpotOrderbookVM { +class SpotOrderBookStore { private readonly rootStore: RootStore; allBuyOrders: SpotMarketOrder[] = []; @@ -208,3 +197,5 @@ class SpotOrderbookVM { return spread.div(maxBuyPrice).times(100).toFormat(2); } } + +export default SpotOrderBookStore; diff --git a/src/stores/SwapStore.ts b/src/stores/SwapStore.ts index 907e6fbb..ee6b5341 100644 --- a/src/stores/SwapStore.ts +++ b/src/stores/SwapStore.ts @@ -1,4 +1,4 @@ -import { FulfillOrderManyParams, GetOrdersParams, LimitType, OrderType } from "@compolabs/spark-orderbook-ts-sdk"; +import { AssetType, GetOrdersParams, LimitType, OrderType } from "@compolabs/spark-orderbook-ts-sdk"; import { autorun, makeAutoObservable, reaction } from "mobx"; import { FuelNetwork } from "@src/blockchain"; @@ -90,6 +90,15 @@ class SwapStore { : "0"; } + getMarketPair = (baseAsset: Token, quoteToken: Token) => { + const { tradeStore } = this.rootStore; + return tradeStore.spotMarkets.find( + (el) => + (el.baseToken.assetId === baseAsset.assetId && el.quoteToken.assetId === quoteToken.assetId) || + (el.baseToken.assetId === quoteToken.assetId && el.quoteToken.assetId === baseAsset.assetId), + ); + }; + updateTokens() { const newTokens = this.fetchNewTokens(); this.tokens = newTokens; @@ -124,7 +133,7 @@ class SwapStore { fetchExchangeFeeDebounce = _.debounce(this.fetchExchangeFee, 250); swapTokens = async ({ slippage }: { slippage: number }): Promise => { - const { notificationStore, tradeStore, oracleStore } = this.rootStore; + const { notificationStore, tradeStore } = this.rootStore; const baseToken = tradeStore.market?.baseToken; const isBuy = baseToken?.assetId === this.buyToken.assetId; const bcNetwork = FuelNetwork.getInstance(); @@ -139,19 +148,42 @@ class SwapStore { orderType: !isBuy ? OrderType.Buy : OrderType.Sell, }); // TODO: check if there is enough price sum to fulfill the order - const formattedAmount = BN.parseUnits(this.payAmount, this.sellToken.decimals).toString(); const formattedVolume = BN.parseUnits(this.receiveAmount, this.buyToken.decimals).toString(); + const decimalToken = isBuy ? this.buyToken.decimals : this.sellToken.decimals; + const depositAmountWithFee = BN.parseUnits(this.exchangeFee, decimalToken).plus( + BN.parseUnits(this.rootStore.tradeStore.matcherFee, decimalToken), + ); - const order: FulfillOrderManyParams = { + // { + // "type": "Sell", + // "amount": "17216", + // "price": "58081620389280", + // "amountToSpend": "17216", + // "amountFee": "15999", + // "depositAssetId": "0x38e4ca985b22625fff93205e997bfc5cc8453a953da638ad297ca60a9f2600bc", + // "feeAssetId": "0x336b7c06352a4b736ff6f688ba6885788b3df16e136e95310ade51aa32dc6f05", + // "assetType": "Base" + // } + const pair = this.getMarketPair(this.buyToken, this.sellToken); + console.log("pair", pair); + if (!pair) return true; + const order = { + type: isBuy ? OrderType.Buy : OrderType.Sell, amount: isBuy ? formattedVolume : formattedAmount, - orderType: isBuy ? OrderType.Buy : OrderType.Sell, - limitType: LimitType.FOK, price: sellOrders[sellOrders.length - 1].price.toString(), + amountToSpend: isBuy ? formattedAmount : formattedVolume, + amountFee: depositAmountWithFee.toString(), + depositAssetId: pair?.baseToken.assetId, + feeAssetId: pair?.quoteToken.assetId, + assetType: isBuy ? AssetType.Quote : AssetType.Base, + limitType: LimitType.FOK, orders: sellOrders.map((el) => el.id), slippage: slippage.toString(), }; + console.log("order", order); + const amountFormatted = BN.formatUnits( isBuy ? formattedVolume : formattedAmount, this.sellToken.decimals, @@ -163,7 +195,8 @@ class SwapStore { ).toSignificant(2); try { - const tx = await bcNetwork.swapTokens(order); + const marketContracts = CONFIG.APP.markets.map((el) => el.contractId); + const tx = await bcNetwork.createSpotOrderWithDeposit(order, marketContracts); notificationStore.success({ text: getActionMessage(ACTION_MESSAGE_TYPE.CREATING_SWAP)( amountFormatted, @@ -171,7 +204,7 @@ class SwapStore { volumeFormatted, this.buyToken.symbol, ), - hash: tx.transactionId, + hash: "tx.transactionId,", }); return true; } catch (error: any) { @@ -230,11 +263,11 @@ class SwapStore { return [ { assetId: market.baseAssetId, - balance: new BN(marketBalance?.locked?.base ?? 0), + balance: new BN(marketBalance?.liquid?.base ?? 0), }, { assetId: market.quoteAssetId, - balance: new BN(marketBalance?.locked?.quote ?? 0), + balance: new BN(marketBalance?.liquid?.quote ?? 0), }, ]; }) diff --git a/src/stores/index.ts b/src/stores/index.ts index 4248ee9b..e2347df2 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -10,6 +10,7 @@ import QuickAssetsStore from "./QuickAssetsStore"; import RootStore from "./RootStore"; import SettingsStore from "./SettingsStore"; import TradeStore from "./TradeStore"; +import SpotOrderBookStore from "./SpotOrderBookStore"; export { AccountStore, @@ -20,6 +21,7 @@ export { QuickAssetsStore, RootStore, SettingsStore, + SpotOrderBookStore, storesContext, // SpotOrdersStore, TradeStore, From 0b8d93d28b640c631465f73ebc9bea129074842a Mon Sep 17 00:00:00 2001 From: Anton-rock Date: Mon, 16 Sep 2024 09:30:54 +0300 Subject: [PATCH 2/2] feat: swap and market orders --- .../RightBlock/CreateOrder/CreateOrderVM.tsx | 45 +++---------------- src/screens/SwapScreen/SwapScreen.tsx | 18 +++++--- src/stores/SwapStore.ts | 40 ++++++++--------- 3 files changed, 38 insertions(+), 65 deletions(-) diff --git a/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrderVM.tsx b/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrderVM.tsx index 8fa5c241..35baa702 100644 --- a/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrderVM.tsx +++ b/src/screens/SpotScreen/RightBlock/CreateOrder/CreateOrderVM.tsx @@ -315,7 +315,6 @@ class CreateOrderVM { ) .map((m) => m.contractId); - console.log("marketContracts", marketContracts); if (bcNetwork.getIsExternalWallet()) { notificationStore.info({ text: "Please, confirm operation in your wallet" }); } @@ -364,9 +363,6 @@ class CreateOrderVM { price: this.inputPrice.toString(), ...deposit, }; - console.log("order", order); - - console.log(order); const data = await bcNetwork.createSpotOrderWithDeposit(order, marketContracts); return data.transactionId; @@ -392,7 +388,7 @@ class CreateOrderVM { const orders = await bcNetwork.fetchSpotOrders({ ...params, orderType: oppositeOrderType }); let total = this.inputTotal; let spend = BN.ZERO; - const arr = orders + const orderList = orders .map((el) => { if (total.toNumber() < 0) { return null; @@ -402,55 +398,26 @@ class CreateOrderVM { return el; }) .filter((el) => el !== null); - console.log("arr", arr); + const price = - settingsStore.orderType === ORDER_TYPE.Market ? arr[arr.length - 1].price.toString() : this.inputPrice.toString(); + settingsStore.orderType === ORDER_TYPE.Market + ? orderList[orderList.length - 1].price.toString() + : this.inputPrice.toString(); - console.log("this.inputAmount", this.inputAmount.toString()); - console.log("price", price.toString()); - console.log("spend", spend.toString()); - // 72275.54 deposit = { ...deposit, amountToSpend: this.inputAmount.toString(), }; - // { - // "amount": "13835", - // "orderType": "Sell", - // "limitType": "FOK", - // "price": "72275538014795", - // "orders": [ - // "0x6d96fca99833848826e6d95786a4e513bb5db72c6d4fc9d554b6464fc1ef97bd" - // ], - // "slippage": "10000", - // "amountToSpend": "13835", - // "amountFee": "16000", - // "depositAssetId": "0x38e4ca985b22625fff93205e997bfc5cc8453a953da638ad297ca60a9f2600bc", - // "feeAssetId": "0x336b7c06352a4b736ff6f688ba6885788b3df16e136e95310ade51aa32dc6f05", - // "assetType": "Base" - // } - - // { - // "type": "Sell", - // "amount": "13835", - // "price": "72275540000000", - // "amountToSpend": "13835", - // "amountFee": "16000", - // "depositAssetId": "0x38e4ca985b22625fff93205e997bfc5cc8453a953da638ad297ca60a9f2600bc", - // "feeAssetId": "0x336b7c06352a4b736ff6f688ba6885788b3df16e136e95310ade51aa32dc6f05", - // "assetType": "Base" - // } const order: FulfillOrderManyWithDepositParams = { amount: this.inputAmount.toString(), orderType: type, limitType: settingsStore.timeInForce, price, - orders: arr.map((el) => el.id), + orders: orderList.map((el) => el.id), slippage: "10000", ...deposit, }; - console.log("order", order); const data = await bcNetwork.fulfillOrderManyWithDeposit(order, marketContracts); return data.transactionId; }; diff --git a/src/screens/SwapScreen/SwapScreen.tsx b/src/screens/SwapScreen/SwapScreen.tsx index 919931f9..2b1744f4 100644 --- a/src/screens/SwapScreen/SwapScreen.tsx +++ b/src/screens/SwapScreen/SwapScreen.tsx @@ -30,7 +30,7 @@ export const SwapScreen: React.FC = observer(() => { const { isConnected } = useWallet(); const theme = useTheme(); const media = useMedia(); - const { swapStore, balanceStore, tradeStore } = useStores(); + const { swapStore, balanceStore, tradeStore, spotOrderBookStore } = useStores(); const [isConnectDialogVisible, openConnectDialog, closeConnectDialog] = useFlag(); const bcNetwork = FuelNetwork.getInstance(); const [slippage, setSlippage] = useState(INITIAL_SLIPPAGE); @@ -85,9 +85,11 @@ export const SwapScreen: React.FC = observer(() => { } swapStore.setPayAmount(newPayAmount); - - const receiveAmount = - Number(newPayAmount) * (parseNumberWithCommas(sellTokenPrice) / parseNumberWithCommas(buyTokenPrice)); + const isBuy = swapStore.isBuy; + const price = !isBuy() + ? spotOrderBookStore.buyOrders[0].price + : spotOrderBookStore.sellOrders[spotOrderBookStore.sellOrders.length - 1].price; + const receiveAmount = BN.parseUnits(new BN(newPayAmount).dividedBy(price), DEFAULT_DECIMALS); swapStore.setReceiveAmount(receiveAmount.toFixed(swapStore.buyToken.precision)); }; @@ -101,8 +103,11 @@ export const SwapScreen: React.FC = observer(() => { swapStore.setReceiveAmount(newReceiveAmount); - const payAmount = - Number(newReceiveAmount) * (parseNumberWithCommas(buyTokenPrice) / parseNumberWithCommas(sellTokenPrice)); + const isBuy = swapStore.isBuy; + const price = !isBuy() + ? spotOrderBookStore.buyOrders[0].price + : spotOrderBookStore.sellOrders[spotOrderBookStore.sellOrders.length - 1].price; + const payAmount = BN.parseUnits(new BN(newReceiveAmount).dividedBy(price), DEFAULT_DECIMALS); swapStore.setPayAmount(payAmount.toFixed(swapStore.sellToken.precision)); }; @@ -131,6 +136,7 @@ export const SwapScreen: React.FC = observer(() => { swapStore.setReceiveAmount("0"); } } catch (err) { + setIsloading(false); console.error("er", err); } }; diff --git a/src/stores/SwapStore.ts b/src/stores/SwapStore.ts index ee6b5341..75627c40 100644 --- a/src/stores/SwapStore.ts +++ b/src/stores/SwapStore.ts @@ -143,7 +143,7 @@ class SwapStore { status: ["Active"], }; - const sellOrders = await bcNetwork!.fetchSpotOrders({ + const orders = await bcNetwork!.fetchSpotOrders({ ...params, orderType: !isBuy ? OrderType.Buy : OrderType.Sell, }); @@ -155,35 +155,35 @@ class SwapStore { BN.parseUnits(this.rootStore.tradeStore.matcherFee, decimalToken), ); - // { - // "type": "Sell", - // "amount": "17216", - // "price": "58081620389280", - // "amountToSpend": "17216", - // "amountFee": "15999", - // "depositAssetId": "0x38e4ca985b22625fff93205e997bfc5cc8453a953da638ad297ca60a9f2600bc", - // "feeAssetId": "0x336b7c06352a4b736ff6f688ba6885788b3df16e136e95310ade51aa32dc6f05", - // "assetType": "Base" - // } const pair = this.getMarketPair(this.buyToken, this.sellToken); - console.log("pair", pair); if (!pair) return true; + let total = new BN(isBuy ? formattedAmount : formattedVolume); + let spend = BN.ZERO; + const orderList = orders + .map((el) => { + if (total.toNumber() < 0) { + return null; + } + spend = spend.plus(el.currentAmount); + total = total.minus(el.currentQuoteAmount); + return el; + }) + .filter((el) => el !== null); + const order = { - type: isBuy ? OrderType.Buy : OrderType.Sell, + orderType: isBuy ? OrderType.Buy : OrderType.Sell, amount: isBuy ? formattedVolume : formattedAmount, - price: sellOrders[sellOrders.length - 1].price.toString(), - amountToSpend: isBuy ? formattedAmount : formattedVolume, + price: orderList[orderList.length - 1].price.toString(), + amountToSpend: isBuy ? formattedVolume : formattedAmount, amountFee: depositAmountWithFee.toString(), - depositAssetId: pair?.baseToken.assetId, + depositAssetId: isBuy ? pair?.quoteToken.assetId : pair?.baseToken.assetId, feeAssetId: pair?.quoteToken.assetId, assetType: isBuy ? AssetType.Quote : AssetType.Base, limitType: LimitType.FOK, - orders: sellOrders.map((el) => el.id), + orders: orderList.map((el) => el.id), slippage: slippage.toString(), }; - console.log("order", order); - const amountFormatted = BN.formatUnits( isBuy ? formattedVolume : formattedAmount, this.sellToken.decimals, @@ -196,7 +196,7 @@ class SwapStore { try { const marketContracts = CONFIG.APP.markets.map((el) => el.contractId); - const tx = await bcNetwork.createSpotOrderWithDeposit(order, marketContracts); + const tx = await bcNetwork.fulfillOrderManyWithDeposit(order, marketContracts); notificationStore.success({ text: getActionMessage(ACTION_MESSAGE_TYPE.CREATING_SWAP)( amountFormatted,