diff --git a/spark-frontend/src/components/Table.tsx b/spark-frontend/src/components/Table.tsx index 0b7e0889..adaee4f2 100644 --- a/spark-frontend/src/components/Table.tsx +++ b/spark-frontend/src/components/Table.tsx @@ -90,6 +90,7 @@ const Table: React.FC = ({ columns, data, onClick, fitContent, withHover const Root = styled.div<{ hovered?: boolean; fitContent?: boolean }>` width: ${({ fitContent }) => (fitContent ? "fit-content" : "100%")}; background: ${({ theme }) => `${theme.colors.bgPrimary}`}; + height: fit-content; table { width: 100%; diff --git a/spark-frontend/src/screens/TradeScreen/BottomTables/BottomTablesInterfaceSpot/BottomTablesInterfaceSpotVM.tsx b/spark-frontend/src/screens/TradeScreen/BottomTables/BottomTablesInterfaceSpot/BottomTablesInterfaceSpotVM.tsx index ad96027b..7e8be70e 100644 --- a/spark-frontend/src/screens/TradeScreen/BottomTables/BottomTablesInterfaceSpot/BottomTablesInterfaceSpotVM.tsx +++ b/spark-frontend/src/screens/TradeScreen/BottomTables/BottomTablesInterfaceSpot/BottomTablesInterfaceSpotVM.tsx @@ -60,6 +60,7 @@ class BottomTablesInterfaceSpotVM { const contract = new ethers.Contract(CONTRACT_ADDRESSES.spotMarket, SPOT_MARKET_ABI, accountStore.signer); const transaction = await contract.removeOrder(orderId); await transaction.wait(); + notificationStore.toast("Order canceled!", { type: "success" }); } catch (error) { handleEvmErrors(notificationStore, error, "We were unable to cancel your order at this time"); } diff --git a/spark-frontend/src/screens/TradeScreen/LeftBlock/CreateOrderSpot/CreateOrderSpot.tsx b/spark-frontend/src/screens/TradeScreen/LeftBlock/CreateOrderSpot/CreateOrderSpot.tsx index adfba6c5..75fd93f3 100644 --- a/spark-frontend/src/screens/TradeScreen/LeftBlock/CreateOrderSpot/CreateOrderSpot.tsx +++ b/spark-frontend/src/screens/TradeScreen/LeftBlock/CreateOrderSpot/CreateOrderSpot.tsx @@ -78,6 +78,22 @@ const CreateOrderSpot: React.FC = observer(({ ...rest }) => { const isInputPriceDisabled = vm.orderType !== ORDER_TYPE.Limit; + const renderButton = () => { + if (!vm.tokenIsApproved) { + return ( + + ); + } + + return ( + + ); + }; + return ( @@ -207,9 +223,7 @@ const CreateOrderSpot: React.FC = observer(({ ...rest }) => { - + {renderButton()} ); }); diff --git a/spark-frontend/src/screens/TradeScreen/LeftBlock/CreateOrderSpot/CreateOrderSpotVM.tsx b/spark-frontend/src/screens/TradeScreen/LeftBlock/CreateOrderSpot/CreateOrderSpotVM.tsx index b107b10f..5b6147b3 100644 --- a/spark-frontend/src/screens/TradeScreen/LeftBlock/CreateOrderSpot/CreateOrderSpotVM.tsx +++ b/spark-frontend/src/screens/TradeScreen/LeftBlock/CreateOrderSpot/CreateOrderSpotVM.tsx @@ -48,6 +48,8 @@ class CreateOrderSpotVM { inputPercent: BN = BN.ZERO; inputTotal: BN = BN.ZERO; + allowance: BN = BN.ZERO; + constructor(private rootStore: RootStore) { makeAutoObservable(this); //todo обработать маркет и лимит типы заказов в селекторе @@ -62,6 +64,14 @@ class CreateOrderSpotVM { } }, ); + + reaction( + () => [this.mode, tradeStore.market], + async () => { + await this.loadAllowance(); + }, + { fireImmediately: true }, + ); } get canProceed() { @@ -78,6 +88,11 @@ class CreateOrderSpotVM { return this.mode === ORDER_MODE.SELL; } + get tokenIsApproved() { + const amount = this.isSell ? this.inputAmount : this.inputTotal; + return this.allowance.gte(amount); + } + setOrderMode = (mode: ORDER_MODE) => (this.mode = mode); onMaxClick = () => { @@ -118,7 +133,7 @@ class CreateOrderSpotVM { setInputAmount = (amount: BN, sync?: boolean) => { const { tradeStore, balanceStore } = this.rootStore; - this.inputAmount = amount; + this.inputAmount = amount.toDecimalPlaces(0); if (!sync) return; @@ -170,6 +185,57 @@ class CreateOrderSpotVM { this.setInputPercent(inputPercent); }; + approve = async () => { + const { accountStore, tradeStore, notificationStore } = this.rootStore; + const { market } = tradeStore; + + if (!accountStore.signer || !market) return; + + const baseToken = market.baseToken; + const quoteToken = market.quoteToken; + + const activeToken = this.isSell ? baseToken : quoteToken; + const approveAmount = this.isSell ? this.inputAmount : this.inputTotal; + + this.setLoading(true); + + try { + const tokenContract = new ethers.Contract(activeToken.assetId, ERC20_ABI, accountStore.signer); + const approveTransaction = await tokenContract.approve(CONTRACT_ADDRESSES.spotMarket, approveAmount.toString()); + + await approveTransaction.wait(); + await this.loadAllowance(); + + notificationStore.toast(`${activeToken.symbol} approved!`, { type: "success" }); + } catch (error) { + notificationStore.toast(`Something goes wrong with ${activeToken.symbol} approve`, { type: "error" }); + } + + this.setLoading(false); + }; + + loadAllowance = async () => { + const { accountStore, tradeStore } = this.rootStore; + const { market } = tradeStore; + + const baseToken = market?.baseToken; + const quoteToken = market?.quoteToken; + + const activeToken = this.isSell ? baseToken : quoteToken; + + if (!activeToken?.assetId) return; + + try { + const tokenContract = new ethers.Contract(activeToken.assetId, ERC20_ABI, accountStore.signer); + const allowance = await tokenContract.allowance(accountStore.address, CONTRACT_ADDRESSES.spotMarket); + + this.allowance = new BN(allowance.toString()); + } catch (error) { + console.error("Something wrong with allowance!"); + this.allowance = BN.ZERO; + } + }; + createOrder = async () => { const { accountStore, tradeStore, notificationStore, balanceStore } = this.rootStore; const { market } = tradeStore; @@ -180,12 +246,7 @@ class CreateOrderSpotVM { try { const baseToken = market.baseToken; - const quoteToken = market.quoteToken; const baseSize = this.isSell ? this.inputAmount.times(-1) : this.inputAmount; - const activeToken = this.isSell ? baseToken : quoteToken; - - const tokenContract = new ethers.Contract(activeToken.assetId, ERC20_ABI, accountStore.signer); - await tokenContract.approve(CONTRACT_ADDRESSES.spotMarket, this.inputTotal.toString()); const spotMarketContract = new ethers.Contract( CONTRACT_ADDRESSES.spotMarket, @@ -200,6 +261,7 @@ class CreateOrderSpotVM { await openOrderTransaction.wait(); notificationStore.toast(); } catch (error: any) { + console.error(error); handleEvmErrors(notificationStore, error, "We were unable to process your order at this time"); }