diff --git a/components/orderbook/OrdersTable.tsx b/components/orderbook/OrdersTable.tsx new file mode 100644 index 000000000..a8110e425 --- /dev/null +++ b/components/orderbook/OrdersTable.tsx @@ -0,0 +1,152 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { InputMaybe, OrderStatus, OrderWhereInput } from "@zeitgeistpm/indexer"; +import { BaseAssetId, ZTG, getIndexOf, isRpcSdk } from "@zeitgeistpm/sdk"; +import SecondaryButton from "components/ui/SecondaryButton"; +import Table, { TableColumn, TableData } from "components/ui/Table"; +import { lookupAssetSymbol } from "lib/constants/foreign-asset"; +import { + ordersRootKey, + useOrders, +} from "lib/hooks/queries/orderbook/useOrders"; +import { useMarketsByIds } from "lib/hooks/queries/useMarketsByIds"; +import { useExtrinsic } from "lib/hooks/useExtrinsic"; +import { useSdkv2 } from "lib/hooks/useSdkv2"; +import { useNotifications } from "lib/state/notifications"; +import { useWallet } from "lib/state/wallet"; +import { parseAssetIdString } from "lib/util/parse-asset-id"; + +const columns: TableColumn[] = [ + { + header: "Outcome", + accessor: "outcome", + type: "component", + }, + { + header: "Side", + accessor: "side", + type: "text", + }, + { + header: "Amount", + accessor: "amount", + type: "text", + }, + { + header: "Price", + accessor: "price", + type: "text", + }, + { + header: "Filled", + accessor: "percentageFilled", + type: "text", + }, + { + header: "Status", + accessor: "status", + type: "text", + }, + { + header: "", + accessor: "button", + type: "component", + width: "180px", + }, +]; + +const OrdersTable = ({ where }: { where: InputMaybe }) => { + const { realAddress } = useWallet(); + const { data: orders } = useOrders(where); + const { data: markets } = useMarketsByIds( + orders?.map((order) => ({ marketId: order.marketId })), + ); + + const tableData: TableData[] | undefined = orders?.map( + ({ + side, + price, + outcomeAssetId, + outcomeAmount, + id, + marketId, + makerAddress, + filledPercentage, + status, + }) => { + const index = getIndexOf(outcomeAssetId); + const market = markets?.find((market) => market.marketId === marketId); + const outcomeName = market?.categories?.[index]?.name; + const baseAsset = parseAssetIdString(market?.baseAsset) as BaseAssetId; + const baseSymbol = lookupAssetSymbol(baseAsset); + const orderFilled = filledPercentage === 100; + + return { + side: side.toUpperCase(), + outcome: outcomeName, + amount: outcomeAmount.div(ZTG).toFixed(2), + value: `${outcomeAmount.mul(price).div(ZTG).toFixed(3)} ${baseSymbol}`, + price: `${price.toFixed(3)} ${baseSymbol}`, + percentageFilled: `${filledPercentage.toFixed(0)}%`, + status: status, + button: ( + + ), + }; + }, + ); + return ( +
+ + + ); +}; + +const CancelOrderButton = ({ + orderId, + disabled, +}: { + orderId: string; + disabled: boolean; +}) => { + const notificationStore = useNotifications(); + const [sdk, id] = useSdkv2(); + const queryClient = useQueryClient(); + + const { + isLoading, + isSuccess, + send: cancelOrder, + } = useExtrinsic( + () => { + if (!isRpcSdk(sdk)) return; + return sdk.api.tx.orderbook.removeOrder(orderId); + }, + { + onSuccess: () => { + queryClient.invalidateQueries([id, ordersRootKey]); + + notificationStore.pushNotification("Successfully cancelled order", { + type: "Success", + }); + }, + }, + ); + + return ( + cancelOrder()} + disabled={isLoading || isSuccess || disabled} + > + Cancel Order + + ); +}; + +export default OrdersTable; diff --git a/components/trade-form/Amm2TradeForm.tsx b/components/trade-form/Amm2TradeForm.tsx index 64c834f1a..e782a7a52 100644 --- a/components/trade-form/Amm2TradeForm.tsx +++ b/components/trade-form/Amm2TradeForm.tsx @@ -1,6 +1,6 @@ import { Tab } from "@headlessui/react"; import { MarketOutcomeAssetId, getIndexOf, ZTG } from "@zeitgeistpm/sdk"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import BuyForm from "./BuyForm"; import SellForm from "./SellForm"; import TradeTab, { TradeTabType } from "./TradeTab"; @@ -10,6 +10,11 @@ import Decimal from "decimal.js"; import { useMarket } from "lib/hooks/queries/useMarket"; import { useAssetMetadata } from "lib/hooks/queries/useAssetMetadata"; import { parseAssetIdString } from "lib/util/parse-asset-id"; +import LimitOrderForm, { + LimitBuyOrderForm, + LimitSellOrderForm, +} from "./LimitOrderForm"; +import { ChevronDown } from "react-feather"; const Amm2TradeForm = ({ marketId, @@ -23,6 +28,7 @@ const Amm2TradeForm = ({ showTabs?: boolean; }) => { const [tabType, setTabType] = useState(); + const [orderType, setOrderType] = useState("market"); const [showSuccessBox, setShowSuccessBox] = useState(false); const [amountReceived, setAmountReceived] = useState(); const [amountIn, setAmountIn] = useState(); @@ -87,49 +93,76 @@ const Amm2TradeForm = ({ }} selectedIndex={tabType} > - - - Buy - - + - Sell - - + + Buy + + + Sell + + + { + setOrderType(type); + }} + value={orderType} + /> + - - { - handleSuccess(data); - setOutcomeAsset(asset); - setAmountIn(amount); - }} - /> - - - { - handleSuccess(data); - setOutcomeAsset(asset); - setAmountIn(amount); - }} - /> - + {orderType === "market" ? ( + <> + + { + handleSuccess(data); + setOutcomeAsset(asset); + setAmountIn(amount); + }} + /> + + + { + handleSuccess(data); + setOutcomeAsset(asset); + setAmountIn(amount); + }} + /> + + + ) : ( + <> + + + + + + + + )} )} @@ -137,4 +170,70 @@ const Amm2TradeForm = ({ ); }; +type OrderType = "market" | "limit"; + +const OrderTypeSelector = ({ + onTypeSelected, + value, +}: { + onTypeSelected: (type: OrderType) => void; + value: OrderType; +}) => { + const [menuOpen, setMenuOpen] = useState(false); + const wrapperRef = useRef(null); + + const handleTypeClick = (type: OrderType) => { + onTypeSelected(type); + setMenuOpen(false); + }; + + useEffect(() => { + const handleClickOutside = (event) => { + if (wrapperRef.current && !wrapperRef.current.contains(event.target)) { + setMenuOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [wrapperRef]); + + return ( +
+ + + {menuOpen && ( +
+ + +
+ )} +
+ ); +}; + export default Amm2TradeForm; diff --git a/components/trade-form/BuyForm.tsx b/components/trade-form/BuyForm.tsx index 3e4f389f3..d86d21c3f 100644 --- a/components/trade-form/BuyForm.tsx +++ b/components/trade-form/BuyForm.tsx @@ -1,3 +1,5 @@ +import { ISubmittableResult } from "@polkadot/types/types"; +import { OrderStatus } from "@zeitgeistpm/indexer"; import { isRpcSdk, MarketOutcomeAssetId, @@ -13,6 +15,7 @@ import { lookupAssetReserve, useAmm2Pool, } from "lib/hooks/queries/amm2/useAmm2Pool"; +import { useOrders } from "lib/hooks/queries/orderbook/useOrders"; import { useAssetMetadata } from "lib/hooks/queries/useAssetMetadata"; import { useBalance } from "lib/hooks/queries/useBalance"; import { useChainConstants } from "lib/hooks/queries/useChainConstants"; @@ -24,18 +27,17 @@ import { useWallet } from "lib/state/wallet"; import { approximateMaxAmountInForBuy, calculateSpotPrice, + calculateSpotPriceAfterBuy, calculateSwapAmountOutForBuy, isValidBuyAmount, } from "lib/util/amm2"; +import { assetsAreEqual } from "lib/util/assets-are-equal"; import { formatNumberCompact } from "lib/util/format-compact"; +import { selectOrdersForMarketBuy } from "lib/util/order-selection"; import { parseAssetIdString } from "lib/util/parse-asset-id"; -import { useState, useEffect, useMemo } from "react"; -import { useForm } from "react-hook-form"; -import { ISubmittableResult } from "@polkadot/types/types"; -import { assetsAreEqual } from "lib/util/assets-are-equal"; import { perbillToNumber } from "lib/util/perbill-to-number"; - -const slippageMultiplier = (100 - DEFAULT_SLIPPAGE_PERCENTAGE) / 100; +import { useEffect, useMemo, useState } from "react"; +import { useForm } from "react-hook-form"; const BuyForm = ({ marketId, @@ -74,6 +76,10 @@ const BuyForm = ({ const baseSymbol = assetMetadata?.symbol; const { data: baseAssetBalance } = useBalance(wallet.realAddress, baseAsset); const { data: pool } = useAmm2Pool(marketId); + const { data: orders } = useOrders({ + marketId_eq: marketId, + status_eq: OrderStatus.Placed, + }); const swapFee = pool?.swapFee.div(ZTG); const creatorFee = new Decimal(perbillToNumber(market?.creatorFee ?? 0)); @@ -117,51 +123,49 @@ const BuyForm = ({ ); }, [assetReserve, pool?.liquidity]); - const { - amountOut, - spotPrice, - newSpotPrice, - priceImpact, - maxProfit, - minAmountOut, - } = useMemo(() => { - const amountOut = - assetReserve && pool.liquidity && swapFee - ? calculateSwapAmountOutForBuy( - assetReserve, - amountIn, - pool.liquidity, - swapFee, - creatorFee, - ) - : new Decimal(0); - - const spotPrice = - assetReserve && calculateSpotPrice(assetReserve, pool?.liquidity); + const { amountOut, spotPrice, newSpotPrice, priceImpact, maxProfit } = + useMemo(() => { + const amountOut = + assetReserve && pool.liquidity && swapFee + ? calculateSwapAmountOutForBuy( + assetReserve, + amountIn, + pool.liquidity, + swapFee, + creatorFee, + ) + : new Decimal(0); - const poolAmountOut = amountOut.minus(amountIn); - const newSpotPrice = - pool?.liquidity && - assetReserve && - calculateSpotPrice(assetReserve?.minus(poolAmountOut), pool?.liquidity); + const spotPrice = + assetReserve && calculateSpotPrice(assetReserve, pool?.liquidity); - const priceImpact = spotPrice - ? newSpotPrice?.div(spotPrice).minus(1).mul(100) - : new Decimal(0); + const newSpotPrice = + pool?.liquidity && + assetReserve && + swapFee && + calculateSpotPriceAfterBuy( + assetReserve, + pool.liquidity, + amountOut, + amountIn, + swapFee, + creatorFee, + ); - const maxProfit = amountOut.minus(amountIn); + const priceImpact = spotPrice + ? newSpotPrice?.div(spotPrice).minus(1).mul(100) + : new Decimal(0); - const minAmountOut = amountOut.mul(slippageMultiplier); + const maxProfit = amountOut.minus(amountIn); - return { - amountOut, - spotPrice, - newSpotPrice, - priceImpact, - maxProfit, - minAmountOut, - }; - }, [amountIn, pool?.liquidity, assetReserve]); + return { + amountOut, + spotPrice, + newSpotPrice, + priceImpact, + maxProfit, + }; + }, [amountIn, pool?.liquidity, assetReserve]); const { isLoading, send, fee } = useExtrinsic( () => { @@ -171,17 +175,38 @@ const BuyForm = ({ !amount || amount === "" || market?.categories?.length == null || - !selectedAsset + !selectedAsset || + !newSpotPrice || + !orders ) { return; } + const amountDecimal = new Decimal(amount).mul(ZTG); // base asset amount + + const maxPrice = newSpotPrice.plus(DEFAULT_SLIPPAGE_PERCENTAGE / 100); // adjust by slippage + const approxOutcomeAmount = amountDecimal.mul(maxPrice); // this will be slightly higher than the expect amount out and therefore may pick up extra order suggestions + + const selectedOrders = selectOrdersForMarketBuy( + maxPrice, + orders + .filter(({ filledPercentage }) => filledPercentage !== 100) + .map(({ id, side, price, outcomeAmount }) => ({ + id: Number(id), + amount: outcomeAmount, + price, + side, + })), + approxOutcomeAmount.abs().mul(ZTG), + ); - return sdk.api.tx.neoSwaps.buy( + return sdk.api.tx.hybridRouter.buy( marketId, market?.categories?.length, selectedAsset, - new Decimal(amount).abs().mul(ZTG).toFixed(0), - minAmountOut.toFixed(0), + amountDecimal.toFixed(0), + maxPrice.mul(ZTG).toFixed(0), + selectedOrders.map(({ id }) => id), + "ImmediateOrCancel", ); }, { @@ -268,7 +293,7 @@ const BuyForm = ({
For
-
+
{ + const [sdk, id] = useSdkv2(); + const notificationStore = useNotifications(); + const wallet = useWallet(); + const { data: orders } = useOrders({ marketId_eq: marketId }); + const { data: market } = useMarket({ + marketId, + }); + const outcomeAssets = market?.outcomeAssets.map( + (assetIdString) => + parseAssetId(assetIdString).unwrap() as MarketOutcomeAssetId, + ); + const [selectedAsset, setSelectedAsset] = useState< + MarketOutcomeAssetId | undefined + >(initialAsset ?? outcomeAssets?.[0]); + const [price, setPrice] = useState(); + const baseAsset = parseAssetIdString(market?.baseAsset); + const queryClient = useQueryClient(); + + const { data: assetMetadata } = useAssetMetadata(baseAsset); + + const { data: baseAssetBalance } = useBalance(wallet.realAddress, baseAsset); + + const maxAmount = baseAssetBalance?.div(price ?? 0) ?? new Decimal(0); + + const { + isLoading, + send: buy, + fee, + } = useExtrinsic<{ + price: Decimal; + amount: Decimal; + }>( + (params) => { + if (!isRpcSdk(sdk) || !market || !selectedAsset || !params) return; + const { price, amount } = params; + const amountIn = amount.mul(price); + return sdk.api.tx.hybridRouter.buy( + marketId, + market.assets.length, + selectedAsset, + amountIn.mul(ZTG).toFixed(0), + price.mul(ZTG).toFixed(0), + [], + "LimitOrder", + ); + }, + { + onSuccess: () => { + queryClient.invalidateQueries([id, ordersRootKey]); + notificationStore.pushNotification(`Placed buy order`, { + type: "Success", + }); + }, + }, + ); + + return ( + { + buy({ price, amount }); + }} + onAssetChange={(asset) => { + setSelectedAsset(asset); + }} + onPriceChange={(price) => { + setPrice(price); + }} + maxAmount={maxAmount} + isLoading={isLoading} + fee={fee} + /> + ); +}; + +export const LimitSellOrderForm = ({ + marketId, + initialAsset, +}: { + marketId: number; + initialAsset?: MarketOutcomeAssetId; +}) => { + const [sdk, id] = useSdkv2(); + const notificationStore = useNotifications(); + const wallet = useWallet(); + const { data: orders } = useOrders({ marketId_eq: marketId }); + const { data: market } = useMarket({ + marketId, + }); + const outcomeAssets = market?.outcomeAssets.map( + (assetIdString) => + parseAssetId(assetIdString).unwrap() as MarketOutcomeAssetId, + ); + const [selectedAsset, setSelectedAsset] = useState< + MarketOutcomeAssetId | undefined + >(initialAsset ?? outcomeAssets?.[0]); + const baseAsset = parseAssetIdString(market?.baseAsset); + const queryClient = useQueryClient(); + + const { data: assetMetadata } = useAssetMetadata(baseAsset); + + const { data: selectedAssetBalance } = useBalance( + wallet.realAddress, + selectedAsset, + ); + + const { + isLoading, + send: sell, + fee, + } = useExtrinsic<{ + price: Decimal; + amount: Decimal; + }>( + (params) => { + if (!isRpcSdk(sdk) || !market || !selectedAsset || !params) return; + const { price, amount } = params; + return sdk.api.tx.hybridRouter.sell( + marketId, + market.assets.length, + selectedAsset, + amount.mul(ZTG).toFixed(0), + price.mul(ZTG).toFixed(0), + [], + "LimitOrder", + ); + }, + { + onSuccess: () => { + queryClient.invalidateQueries([id, ordersRootKey]); + notificationStore.pushNotification(`Placed sell order`, { + type: "Success", + }); + }, + }, + ); + + return ( + { + sell({ price, amount }); + }} + onAssetChange={(asset) => { + setSelectedAsset(asset); + }} + maxAmount={selectedAssetBalance} + isLoading={isLoading} + fee={fee} + /> + ); +}; + +const LimitOrderForm = ({ + marketId, + asset, + onAssetChange, + onPriceChange, + onSubmit, + maxAmount, + side, + isLoading, + fee, +}: { + marketId: number; + asset?: MarketOutcomeAssetId; + maxPrice?: Decimal; + minPrice?: Decimal; + maxAmount?: Decimal; + side: "buy" | "sell"; + isLoading: boolean; + fee?: FeeAsset | null; + onSubmit: (price: Decimal, amount: Decimal) => void; + onAssetChange?: (assetId: MarketOutcomeAssetId) => void; + onPriceChange?: (price: Decimal) => void; +}) => { + const { + register, + handleSubmit, + getValues, + formState, + watch, + setValue, + trigger, + } = useForm({ + reValidateMode: "onChange", + mode: "onChange", + }); + + const { data: market } = useMarket({ + marketId, + }); + const wallet = useWallet(); + const baseAsset = parseAssetIdString(market?.baseAsset); + const { data: pool } = useAmm2Pool(marketId); + + const { data: spotPrices } = useMarketSpotPrices(marketId); + const [initialPriceSetAsset, setInitialPriceSetAsset] = useState< + MarketOutcomeAssetId | undefined + >(); + const spotPrice = asset ? spotPrices?.get(getIndexOf(asset)) : undefined; + + useEffect(() => { + // default price to current spot price + if (!assetsAreEqual(initialPriceSetAsset, asset)) { + const adjustedPrice = spotPrice?.plus( + side === "buy" ? -DEFAULT_PRICE_ADJUSTMENT : DEFAULT_PRICE_ADJUSTMENT, + ); + setValue("price", adjustedPrice?.toFixed(3)); + setInitialPriceSetAsset(asset); + onPriceChange?.(adjustedPrice ?? new Decimal(0)); + trigger("price"); // reset validation + } + }, [spotPrice, initialPriceSetAsset]); + + const { data: assetMetadata } = useAssetMetadata(baseAsset); + const baseSymbol = assetMetadata?.symbol; + + const outcomeAssets = market?.outcomeAssets.map( + (assetIdString) => + parseAssetId(assetIdString).unwrap() as MarketOutcomeAssetId, + ); + + useEffect(() => { + const subscription = watch((value, { name, type }) => { + const changedByUser = type != null; + + if (name === "price" && value.price !== "") { + onPriceChange?.(new Decimal(value.price ?? 0)); + } + if (!changedByUser || !maxAmount) return; + + if (name === "percentage") { + const max = maxAmount; + + setValue( + "amount", + Number( + max + .mul(value.percentage) + .abs() + .div(100) + .div(ZTG) + .toFixed(3, Decimal.ROUND_DOWN), + ), + ); + } else if (name === "amount" && value.amount !== "") { + setValue( + "percentage", + new Decimal(value.amount).mul(ZTG).div(maxAmount).mul(100).toString(), + ); + } + trigger("amount"); + }); + return () => subscription.unsubscribe(); + }, [watch, maxAmount]); + + const amount = new Decimal(getValues("amount") || 0); + const total = amount.mul(getValues("price") || 0); + const maxProfit = amount.minus(total); + + return ( +
+
{ + onSubmit( + new Decimal(value["price"] || 0), + new Decimal(value["amount"] || 0), + ); + })} + className="flex w-full flex-col gap-y-4" + > +
+
Amount
+
+ { + if (value > (maxAmount?.div(ZTG).toNumber() ?? 0)) { + return `Insufficient balance. Max: ${maxAmount + ?.div(ZTG) + .toFixed(1)}`; + } else if (value <= 0) { + return "Amount must be greater than 0"; + } + }, + })} + /> +
+ {market && asset && ( + { + onAssetChange?.(assetId); + }} + /> + )} +
+
+
+
+
Price
+
+ { + if (Number(value) >= 1) { + return `Price must be less than 1`; + } else if (Number(value) <= 0) { + return `Price must be greater than 0`; + } + }, + })} + /> +
{baseSymbol}
+
+
+ + +
+
+ <>{Object.values(formState.errors)[0]?.message} +
+
+
Total:
+
+ {total.toFixed(2)} {baseSymbol} +
+
+ {side === "buy" && ( +
+
Max Profit:
+
+ {maxProfit.toFixed(2)} {baseSymbol} +
+
+ )} +
+
+ +
+
+ {side === "buy" ? "Place Buy Order" : "Place Sell Order"} +
+
+ Network fee:{" "} + {formatNumberCompact(fee?.amount.div(ZTG).toNumber() ?? 0)}{" "} + {fee?.symbol} +
+
+
+
+ +
+ ); +}; + +export default LimitOrderForm; diff --git a/components/trade-form/SellForm.tsx b/components/trade-form/SellForm.tsx index 51d000c90..a476876da 100644 --- a/components/trade-form/SellForm.tsx +++ b/components/trade-form/SellForm.tsx @@ -1,3 +1,5 @@ +import { ISubmittableResult } from "@polkadot/types/types"; +import { OrderStatus } from "@zeitgeistpm/indexer"; import { isRpcSdk, MarketOutcomeAssetId, @@ -13,6 +15,8 @@ import { lookupAssetReserve, useAmm2Pool, } from "lib/hooks/queries/amm2/useAmm2Pool"; +import { useOrders } from "lib/hooks/queries/orderbook/useOrders"; +import { useAssetMetadata } from "lib/hooks/queries/useAssetMetadata"; import { useBalance } from "lib/hooks/queries/useBalance"; import { useChainConstants } from "lib/hooks/queries/useChainConstants"; import { useMarket } from "lib/hooks/queries/useMarket"; @@ -27,12 +31,11 @@ import { isValidSellAmount, } from "lib/util/amm2"; import { formatNumberCompact } from "lib/util/format-compact"; +import { selectOrdersForMarketSell } from "lib/util/order-selection"; import { parseAssetIdString } from "lib/util/parse-asset-id"; -import { useState, useEffect, useMemo } from "react"; -import { useForm } from "react-hook-form"; -import { ISubmittableResult } from "@polkadot/types/types"; import { perbillToNumber } from "lib/util/perbill-to-number"; -import { useAssetMetadata } from "lib/hooks/queries/useAssetMetadata"; +import { useEffect, useMemo, useState } from "react"; +import { useForm } from "react-hook-form"; const slippageMultiplier = (100 - DEFAULT_SLIPPAGE_PERCENTAGE) / 100; @@ -72,6 +75,10 @@ const SellForm = ({ const baseAsset = parseAssetIdString(market?.baseAsset); const { data: assetMetadata } = useAssetMetadata(baseAsset); const baseSymbol = assetMetadata?.symbol; + const { data: orders } = useOrders({ + marketId_eq: marketId, + status_eq: OrderStatus.Placed, + }); const swapFee = pool?.swapFee.div(ZTG); const creatorFee = new Decimal(perbillToNumber(market?.creatorFee ?? 0)); @@ -157,17 +164,36 @@ const SellForm = ({ !amount || amount === "" || market?.categories?.length == null || - !selectedAsset + !selectedAsset || + !newSpotPrice || + !orders ) { return; } - return sdk.api.tx.neoSwaps.sell( + const minPrice = newSpotPrice.mul(slippageMultiplier); // adjust by slippage + + const selectedOrders = selectOrdersForMarketSell( + minPrice, + orders + .filter(({ filledPercentage }) => filledPercentage !== 100) + .map(({ id, side, price, outcomeAmount }) => ({ + id: Number(id), + amount: outcomeAmount, + price, + side, + })), + new Decimal(amount).abs().mul(ZTG), + ); + + return sdk.api.tx.hybridRouter.sell( marketId, market?.categories?.length, selectedAsset, new Decimal(amount).mul(ZTG).toFixed(0), - minAmountOut.toFixed(0), + minPrice.mul(ZTG).toFixed(0), + selectedOrders.map(({ id }) => id), + "ImmediateOrCancel", ); }, { @@ -231,7 +257,7 @@ const SellForm = ({ onSubmit={handleSubmit(onSubmit)} className="flex w-full flex-col items-center gap-y-4" > -
+
diff --git a/lib/hooks/queries/orderbook/useConnectedAddressOrders.ts b/lib/hooks/queries/orderbook/useConnectedAddressOrders.ts new file mode 100644 index 000000000..60a0f0a2b --- /dev/null +++ b/lib/hooks/queries/orderbook/useConnectedAddressOrders.ts @@ -0,0 +1,38 @@ +import { useQuery } from "@tanstack/react-query"; +import { AssetId, isRpcSdk } from "@zeitgeistpm/sdk"; +import Decimal from "decimal.js"; +import { useSdkv2 } from "lib/hooks/useSdkv2"; +import { useWallet } from "lib/state/wallet"; +import { useOrders } from "./useOrders"; + +export const userOrdersRootKey = "user-orders"; + +export type MarketOrder = { + id: number; + makerAddress: string; + makerAmount: Decimal; + makerAsset: AssetId; + marketId: number; + takerAmount: Decimal; + takerAsset: AssetId; +}; + +export const useUserOrders = () => { + const [sdk, id] = useSdkv2(); + + const { realAddress } = useWallet(); + const { data: orders } = useOrders(); + + const query = useQuery( + [id, userOrdersRootKey, orders?.length], + async () => { + return orders?.filter((order) => order.makerAddress === realAddress); + }, + { + enabled: Boolean(sdk && isRpcSdk(sdk)), + staleTime: 10_000, + }, + ); + + return query; +}; diff --git a/lib/hooks/queries/orderbook/useOrders.ts b/lib/hooks/queries/orderbook/useOrders.ts new file mode 100644 index 000000000..6e67b1dec --- /dev/null +++ b/lib/hooks/queries/orderbook/useOrders.ts @@ -0,0 +1,102 @@ +import { useQuery } from "@tanstack/react-query"; +import { InputMaybe, OrderStatus, OrderWhereInput } from "@zeitgeistpm/indexer"; +import { + AssetId, + IOBaseAssetId, + IOMarketOutcomeAssetId, + MarketId, + isIndexedSdk, + parseAssetId, +} from "@zeitgeistpm/sdk"; +import Decimal from "decimal.js"; +import { useSdkv2 } from "lib/hooks/useSdkv2"; + +export const ordersRootKey = "orders"; + +export type Order = { + id: string; + marketId: number; + makerAddress: string; + side: "buy" | "sell"; + price: Decimal; + outcomeAmount: Decimal; + outcomeAssetId: + | { + CategoricalOutcome: [MarketId, number]; + } + | { + ScalarOutcome: [MarketId, "Short" | "Long"]; + }; + filledPercentage: number; + status: OrderStatus; +}; + +export const useOrders = (where?: InputMaybe) => { + const [sdk, id] = useSdkv2(); + const enabled = !!sdk && !!isIndexedSdk(sdk); + + const query = useQuery( + [id, ordersRootKey, where], + async () => { + if (enabled) { + const { orders } = await sdk.indexer.orders({ where }); + const ordersMapped: Order[] = orders.map((order) => { + const makerAsset = parseAssetId( + order.maker.asset, + ).unwrap() as unknown as AssetId; + const takerAsset = parseAssetId( + order.taker.asset, + ).unwrap() as unknown as AssetId; + + const side = IOBaseAssetId.is(makerAsset) ? "buy" : "sell"; + const takerInitialAmount = new Decimal(order.taker.filledAmount).plus( + order.taker.unfilledAmount, + ); + const makerInitialAmount = new Decimal(order.maker.filledAmount).plus( + order.maker.unfilledAmount, + ); + const price = IOBaseAssetId.is(makerAsset) + ? makerInitialAmount.div(takerInitialAmount) + : takerInitialAmount.div(makerInitialAmount); + + const outcomeAssetId = IOMarketOutcomeAssetId.is(makerAsset) + ? makerAsset + : IOMarketOutcomeAssetId.is(takerAsset) + ? takerAsset + : undefined; + + const outcomeAmount = IOBaseAssetId.is(makerAsset) + ? takerInitialAmount + : makerInitialAmount; + + const filledPercentage = new Decimal(order.taker.filledAmount) + .div(takerInitialAmount) + .mul(100) + .toNumber(); + + const mappedOrder: Order = { + id: order.id, + marketId: order.marketId, + makerAddress: order.makerAccountId, + side, + price, + outcomeAmount: outcomeAmount, + outcomeAssetId: outcomeAssetId!, // one of the assets must be MarketOutcome + filledPercentage, + status: order.status, + }; + + return mappedOrder; + }); + + return ordersMapped; + } + }, + { + enabled, + staleTime: 10_000, + }, + ); + + return query; +}; diff --git a/lib/hooks/queries/orderbook/useRpcOrders.ts b/lib/hooks/queries/orderbook/useRpcOrders.ts new file mode 100644 index 000000000..2046c3436 --- /dev/null +++ b/lib/hooks/queries/orderbook/useRpcOrders.ts @@ -0,0 +1,96 @@ +import { useQuery } from "@tanstack/react-query"; +import { + AssetId, + IOBaseAssetId, + IOMarketOutcomeAssetId, + MarketId, + isRpcSdk, + parseAssetId, +} from "@zeitgeistpm/sdk"; +import Decimal from "decimal.js"; +import { useSdkv2 } from "lib/hooks/useSdkv2"; + +export const rpcOrdersRootKey = "rpc-orders"; + +type RawMarketOrderData = { + makerAmount: Decimal; + makerAsset: AssetId; + takerAmount: Decimal; + takerAsset: AssetId; +}; + +export type MarketOrder = { + id: number; + marketId: number; + makerAddress: string; + raw: RawMarketOrderData; + side: "buy" | "sell"; + price: Decimal; + outcomeAmount: Decimal; + outcomeAssetId: + | { + CategoricalOutcome: [MarketId, number]; + } + | { + ScalarOutcome: [MarketId, "Short" | "Long"]; + }; +}; + +export const useRpcOrders = () => { + const [sdk, id] = useSdkv2(); + + const query = useQuery( + [id, rpcOrdersRootKey], + async () => { + if (isRpcSdk(sdk)) { + const ordersRes = await sdk.api.query.orderbook.orders.entries(); + const orders: MarketOrder[] = ordersRes.map(([a, b]) => { + const chainOrder = b.unwrap(); + const rawData: RawMarketOrderData = { + makerAmount: new Decimal(chainOrder.makerAmount.toString()), + makerAsset: parseAssetId( + chainOrder.makerAsset, + ).unwrap() as unknown as AssetId, + takerAmount: new Decimal(chainOrder.takerAmount.toString()), + takerAsset: parseAssetId( + chainOrder.takerAsset, + ).unwrap() as unknown as AssetId, + }; + + const side = IOBaseAssetId.is(rawData.makerAsset) ? "buy" : "sell"; + const price = IOBaseAssetId.is(rawData.makerAsset) + ? rawData.takerAmount.div(rawData.makerAmount) + : rawData.makerAmount.div(rawData.takerAmount); + const outcomeAssetId = IOMarketOutcomeAssetId.is(rawData.makerAsset) + ? rawData.makerAsset + : IOMarketOutcomeAssetId.is(rawData.takerAsset) + ? rawData.takerAsset + : undefined; + const outcomeAmount = IOBaseAssetId.is(rawData.makerAsset) + ? rawData.takerAmount + : rawData.makerAmount; + + const order: MarketOrder = { + id: Number(a[0].toString()), + marketId: Number(chainOrder.marketId.toString()), + makerAddress: chainOrder.maker.toString(), + raw: rawData, + side, + price, + outcomeAmount, + outcomeAssetId: outcomeAssetId!, // one of the assets must be MarketOutcome + }; + + return order; + }); + return orders; + } + }, + { + enabled: Boolean(sdk && isRpcSdk(sdk)), + staleTime: 10_000, + }, + ); + + return query; +}; diff --git a/lib/hooks/queries/useFeePayingAsset.ts b/lib/hooks/queries/useFeePayingAsset.ts index f5dca611f..a109062c6 100644 --- a/lib/hooks/queries/useFeePayingAsset.ts +++ b/lib/hooks/queries/useFeePayingAsset.ts @@ -9,7 +9,7 @@ import { CurrencyBalance } from "./useCurrencyBalances"; import { useForeignAssetBalances } from "./useForeignAssetBalances"; import { useZtgBalance } from "./useZtgBalance"; -type FeeAsset = { +export type FeeAsset = { assetId: AssetId; symbol: string; amount: Decimal; diff --git a/lib/util/amm2.spec.ts b/lib/util/amm2.spec.ts index 3e6704f6f..29961c2b1 100644 --- a/lib/util/amm2.spec.ts +++ b/lib/util/amm2.spec.ts @@ -9,6 +9,8 @@ import { isValidBuyAmount, isValidSellAmount, calculateReserveAfterSell, + calculateSpotPriceAfterBuy, + calculateSpotPriceAfterSell, } from "./amm2"; import { ZTG } from "@zeitgeistpm/sdk"; @@ -83,6 +85,33 @@ describe("amm2", () => { }); }); + describe("calculateSpotPriceAfterBuy", () => { + test("should work", () => { + const spotPrice = calculateSpotPriceAfterBuy( + new Decimal(59.00001516623987), + new Decimal(144.00003701590745), + new Decimal(538.4164567924635), + new Decimal(486), + new Decimal(0.01), + new Decimal(0), + ); + + expect(spotPrice.toNumber()).toEqual(0.9881021930750705); + }); + }); + describe("calculateSpotPriceAfterSell", () => { + test("should work", () => { + const spotPrice = calculateSpotPriceAfterSell( + new Decimal(99.0), + new Decimal(108.04431012579187), + new Decimal(40), + new Decimal(14.27511827415865), + ); + + expect(spotPrice.toFixed(8)).toEqual("0.31525092"); + }); + }); + describe("approximateMaxAmountInForBuy", () => { //seems like correct number would be 41 test("should work", () => { diff --git a/lib/util/amm2.ts b/lib/util/amm2.ts index d948b4664..b86afb882 100644 --- a/lib/util/amm2.ts +++ b/lib/util/amm2.ts @@ -59,6 +59,37 @@ export const calculateSpotPrice = ( return new Decimal(0).minus(reserve).div(liquidity).exp(); }; +export const calculateSpotPriceAfterBuy = ( + initialReserve: Decimal, // amount of asset in the pool + liquidity: Decimal, // liqudity parameter of the pool + outcomeAssetOut: Decimal, + baseAssetAmountIn: Decimal, + poolFee: Decimal, // 1% is 0.01 + creatorFee: Decimal, // 1% is 0.01 +) => { + const amountInMinusFee = baseAssetAmountIn.mul( + new Decimal(1).minus(poolFee.plus(creatorFee)), + ); + + const newReserve = initialReserve.minus( + outcomeAssetOut.minus(amountInMinusFee), + ); + + return calculateSpotPrice(newReserve, liquidity); +}; + +export const calculateSpotPriceAfterSell = ( + initialReserve: Decimal, // amount of asset in the pool + liquidity: Decimal, // liqudity parameter of the pool + outcomeAssetIn: Decimal, + baseAssetAmountOut: Decimal, +) => { + const newReserve = initialReserve.plus( + outcomeAssetIn.minus(baseAssetAmountOut), + ); + return calculateSpotPrice(newReserve, liquidity); +}; + export const approximateMaxAmountInForBuy = ( reserve: Decimal, // amount of asset in the pool liquidity: Decimal, // liqudity parameter of the pool diff --git a/lib/util/order-selection.spec.ts b/lib/util/order-selection.spec.ts new file mode 100644 index 000000000..d2e89947b --- /dev/null +++ b/lib/util/order-selection.spec.ts @@ -0,0 +1,119 @@ +import Decimal from "decimal.js"; +import { describe, expect, test } from "vitest"; +import { + MarketOrder, + selectOrdersForMarketBuy, + selectOrdersForMarketSell, +} from "./order-selection"; + +const ordersMock1: MarketOrder[] = [ + { + id: 1, + price: new Decimal(0.8), + amount: new Decimal(100), + side: "sell", + }, + { + id: 1, + price: new Decimal(0.7), + amount: new Decimal(100), + side: "sell", + }, + { + id: 1, + price: new Decimal(0.6), + amount: new Decimal(100), + side: "sell", + }, + { + id: 1, + price: new Decimal(0.4), + amount: new Decimal(100), + side: "buy", + }, + { + id: 1, + price: new Decimal(0.3), + amount: new Decimal(100), + side: "buy", + }, + { + id: 1, + price: new Decimal(0.2), + amount: new Decimal(100), + side: "buy", + }, +]; + +describe("order selection", () => { + describe("selectOrdersForMarketBuy", () => { + test("should select all orders", () => { + const orders = selectOrdersForMarketBuy( + new Decimal(0.9), + ordersMock1, + new Decimal(1000), + ); + + expect(orders.length).toEqual(3); + }); + + test("should select some orders if constrained by price", () => { + const orders = selectOrdersForMarketBuy( + new Decimal(0.7), + ordersMock1, + new Decimal(1000), + ); + + expect(orders.length).toEqual(2); + }); + + test("should select some orders if constrained by amount", () => { + const orders = selectOrdersForMarketBuy( + new Decimal(1), + ordersMock1, + new Decimal(100), + ); + + const { price, amount, side } = orders[0]; + expect(price.toNumber()).toEqual(0.6); + expect(amount.toNumber()).toEqual(100); + expect(side).toEqual("sell"); + expect(orders.length).toEqual(1); + }); + }); + describe("selectOrdersForMarketBuy", () => { + test("should select all orders", () => { + const orders = selectOrdersForMarketSell( + new Decimal(0.1), + ordersMock1, + new Decimal(1000), + ); + + expect(orders.length).toEqual(3); + }); + + test("should select some orders if constrained by price", () => { + const orders = selectOrdersForMarketSell( + new Decimal(0.4), + ordersMock1, + new Decimal(1000), + ); + + expect(orders.length).toEqual(1); + }); + + test("should select some orders if constrained by amount", () => { + const orders = selectOrdersForMarketSell( + new Decimal(0), + ordersMock1, + new Decimal(100), + ); + + const { price, amount, side } = orders[0]; + expect(price.toNumber()).toEqual(0.4); + expect(amount.toNumber()).toEqual(100); + expect(side).toEqual("buy"); + expect(orders.length).toEqual(1); + }); + }); +}); diff --git a/lib/util/order-selection.ts b/lib/util/order-selection.ts new file mode 100644 index 000000000..ae493b56c --- /dev/null +++ b/lib/util/order-selection.ts @@ -0,0 +1,58 @@ +import Decimal from "decimal.js"; + +export type MarketOrder = { + id: number; + price: Decimal; + amount: Decimal; + side: "buy" | "sell"; +}; + +export const selectOrdersForMarketBuy = ( + endingPrice: Decimal, + assetOrderBook: MarketOrder[], // orders for asset to buy/sell + outcomeAssetamount: Decimal, +) => { + const sortedOrders = assetOrderBook.sort((a, b) => + a.price.minus(b.price).toNumber(), + ); + const orderCandidates = sortedOrders.filter( + (order) => + order.side === "sell" && order.price.lessThanOrEqualTo(endingPrice), + ); + + let filledAmount = new Decimal(0); + const selectedOrders: MarketOrder[] = []; + orderCandidates.forEach((order) => { + if (filledAmount.lessThan(outcomeAssetamount)) { + filledAmount = filledAmount.plus(order.amount); + selectedOrders.push(order); + } + }); + + return selectedOrders; +}; + +export const selectOrdersForMarketSell = ( + endingPrice: Decimal, + assetOrderBook: MarketOrder[], // orders for asset to buy/sell + outcomeAssetamount: Decimal, +) => { + const sortedOrders = assetOrderBook.sort((a, b) => + b.price.minus(a.price).toNumber(), + ); + const orderCandidates = sortedOrders.filter( + (order) => + order.side === "buy" && order.price.greaterThanOrEqualTo(endingPrice), + ); + + let filledAmount = new Decimal(0); + const selectedOrders: MarketOrder[] = []; + orderCandidates.forEach((order) => { + if (filledAmount.lessThan(outcomeAssetamount)) { + filledAmount = filledAmount.plus(order.amount); + selectedOrders.push(order); + } + }); + + return selectedOrders; +}; diff --git a/package.json b/package.json index 73bac2a36..8de939b56 100644 --- a/package.json +++ b/package.json @@ -47,11 +47,11 @@ "@web3auth/openlogin-adapter": "^8.0.1", "@yornaath/batshit": "^0.8.0", "@yornaath/batshit-devtools-react": "^0.5.4", - "@zeitgeistpm/augment-api": "3.7.0", + "@zeitgeistpm/augment-api": "3.8.0", "@zeitgeistpm/avatara-nft-sdk": "^1.3.1", "@zeitgeistpm/avatara-util": "^1.2.0", - "@zeitgeistpm/sdk": "3.7.0", - "@zeitgeistpm/utility": "3.7.0", + "@zeitgeistpm/sdk": "3.8.0", + "@zeitgeistpm/utility": "3.8.0", "axios": "^0.21.4", "boring-avatars": "^1.6.1", "decimal.js": "^10.4.3", diff --git a/pages/markets/[marketid].tsx b/pages/markets/[marketid].tsx index 5f0a5b2a2..f88952908 100644 --- a/pages/markets/[marketid].tsx +++ b/pages/markets/[marketid].tsx @@ -1,10 +1,6 @@ import { Disclosure, Tab, Transition } from "@headlessui/react"; import { useQuery } from "@tanstack/react-query"; -import { - FullMarketFragment, - MarketStatus, - ScoringRule, -} from "@zeitgeistpm/indexer"; +import { FullMarketFragment, MarketStatus } from "@zeitgeistpm/indexer"; import { MarketOutcomeAssetId, ScalarRangeType, @@ -26,6 +22,7 @@ import PoolDeployer from "components/markets/PoolDeployer"; import ReportResult from "components/markets/ReportResult"; import ScalarPriceRange from "components/markets/ScalarPriceRange"; import MarketMeta from "components/meta/MarketMeta"; +import OrdersTable from "components/orderbook/OrdersTable"; import CategoricalDisputeBox from "components/outcomes/CategoricalDisputeBox"; import CategoricalReportBox from "components/outcomes/CategoricalReportBox"; import ScalarDisputeBox from "components/outcomes/ScalarDisputeBox"; @@ -52,6 +49,7 @@ import { } from "lib/gql/markets"; import { getResolutionTimestamp } from "lib/gql/resolution-date"; import { useMarketCaseId } from "lib/hooks/queries/court/useMarketCaseId"; +import { useOrders } from "lib/hooks/queries/orderbook/useOrders"; import { useAssetMetadata } from "lib/hooks/queries/useAssetMetadata"; import { useChainConstants } from "lib/hooks/queries/useChainConstants"; import { useMarket } from "lib/hooks/queries/useMarket"; @@ -214,6 +212,11 @@ const Market: NextPage = ({ const router = useRouter(); const { marketid } = router.query; const marketId = Number(marketid); + const { realAddress } = useWallet(); + const { data: orders, isLoading: isOrdersLoading } = useOrders({ + marketId_eq: marketId, + makerAccountId_eq: realAddress, + }); const referendumChain = cmsMetadata?.referendumRef?.chain; const referendumIndex = cmsMetadata?.referendumRef?.referendumIndex; @@ -391,7 +394,7 @@ const Market: NextPage = ({ Twitch Stream @@ -473,7 +476,19 @@ const Market: NextPage = ({
- + {realAddress && + isOrdersLoading === false && + (orders?.length ?? 0) > 0 && ( +
+
My Orders
+ +
+ )} {marketIsLoading === false && marketHasPool === false && (
@@ -576,7 +591,13 @@ const Market: NextPage = ({
-
+
{market?.status === MarketStatus.Active ? ( <> diff --git a/pages/portfolio/[address].tsx b/pages/portfolio/[address].tsx index f422999d2..3aaf0d58c 100644 --- a/pages/portfolio/[address].tsx +++ b/pages/portfolio/[address].tsx @@ -1,5 +1,6 @@ import { Tab } from "@headlessui/react"; import { getIndexOf } from "@zeitgeistpm/sdk"; +import OrdersTable from "components/orderbook/OrdersTable"; import AccountPoolsTable from "components/portfolio/AccountPoolsTable"; import BondsTable from "components/portfolio/BondsTable"; import { PortfolioBreakdown } from "components/portfolio/Breakdown"; @@ -36,11 +37,16 @@ const mainTabItems: MainTabItem[] = [ "Court", ] as MainTabItem[]; -type MarketsTabItem = "Created Markets" | "Liquidity" | "Creator Fee Payouts"; +type MarketsTabItem = + | "Created Markets" + | "Liquidity" + | "Creator Fee Payouts" + | "Orders"; const marketsTabItems: MarketsTabItem[] = [ "Created Markets", "Liquidity", "Creator Fee Payouts", + "Orders", ]; const Portfolio: NextPageWithLayout = () => { @@ -204,6 +210,15 @@ const Portfolio: NextPageWithLayout = () => { {address && } + + {address && ( + + )} + diff --git a/yarn.lock b/yarn.lock index 9255b80e3..49537c63b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4513,14 +4513,14 @@ __metadata: languageName: node linkType: hard -"@zeitgeistpm/augment-api@npm:3.7.0, @zeitgeistpm/augment-api@npm:^3.7.0": - version: 3.7.0 - resolution: "@zeitgeistpm/augment-api@npm:3.7.0" +"@zeitgeistpm/augment-api@npm:3.8.0, @zeitgeistpm/augment-api@npm:^3.8.0": + version: 3.8.0 + resolution: "@zeitgeistpm/augment-api@npm:3.8.0" peerDependencies: "@polkadot/api-base": "*" "@polkadot/rpc-core": "*" "@polkadot/types": "*" - checksum: ca2658b680406a10c548c159edad4a41fec8f77cad356df243718a3ef779a718c92596243e7161da77931c7563de9f28deaacd13f680143884087279b2ab1f19 + checksum: acffb56ea53bb0a445f83f411d825c927476ac8915a2602ca2a95c2fc8f6ccde98284ad3ea12ef533aef9b410d8467b8cf3b37cf0fc902787dd672c38671233c languageName: node linkType: hard @@ -4575,40 +4575,40 @@ __metadata: languageName: node linkType: hard -"@zeitgeistpm/indexer@npm:^4.7.0": - version: 4.7.0 - resolution: "@zeitgeistpm/indexer@npm:4.7.0" +"@zeitgeistpm/indexer@npm:^4.8.0": + version: 4.8.0 + resolution: "@zeitgeistpm/indexer@npm:4.8.0" dependencies: graphql: ^16.6.0 graphql-request: ^5.0.0 graphql-tag: ^2.12.6 - checksum: 03c44382e856bc07f2e145dc784a16fbd0a4792c58de83ba289485283b1048904468b8ddfc347675f75af6b5e63bee08828fa4b70421fcc804826f6b28d44851 + checksum: 20040980e15c9564231e851e64ad71e5febf931b97a520a416f98d4705fca4f04628b4ccaee14ba6182ac456c028eb6482d686cb0bb08b0d4606bf96b87c18c7 languageName: node linkType: hard -"@zeitgeistpm/rpc@npm:^3.7.0": - version: 3.7.0 - resolution: "@zeitgeistpm/rpc@npm:3.7.0" +"@zeitgeistpm/rpc@npm:^3.8.0": + version: 3.8.0 + resolution: "@zeitgeistpm/rpc@npm:3.8.0" dependencies: - "@zeitgeistpm/augment-api": ^3.7.0 - "@zeitgeistpm/utility": ^3.7.0 + "@zeitgeistpm/augment-api": ^3.8.0 + "@zeitgeistpm/utility": ^3.8.0 peerDependencies: "@polkadot/api": "*" "@polkadot/keyring": "*" "@polkadot/types": "*" - checksum: 7caf29450310ca8224f1afbd17184836564aa4e62a8dee85d05235d252720f43d0ed99481d5011bda226f870ac0f7db9aa0a2629644876aae63812170d9f1d12 + checksum: 003c5e3c3690ff84076fb22a1619ba010d55d7e2dc274a3f081e5b166da5be40003f5fa066b8d9ce89481105e924c7e317a61ea3f7b7379c25103500712d2a45 languageName: node linkType: hard -"@zeitgeistpm/sdk@npm:3.7.0": - version: 3.7.0 - resolution: "@zeitgeistpm/sdk@npm:3.7.0" +"@zeitgeistpm/sdk@npm:3.8.0": + version: 3.8.0 + resolution: "@zeitgeistpm/sdk@npm:3.8.0" dependencies: - "@zeitgeistpm/augment-api": ^3.7.0 - "@zeitgeistpm/indexer": ^4.7.0 - "@zeitgeistpm/rpc": ^3.7.0 - "@zeitgeistpm/utility": ^3.7.0 - "@zeitgeistpm/web3.storage": ^3.7.0 + "@zeitgeistpm/augment-api": ^3.8.0 + "@zeitgeistpm/indexer": ^4.8.0 + "@zeitgeistpm/rpc": ^3.8.0 + "@zeitgeistpm/utility": ^3.8.0 + "@zeitgeistpm/web3.storage": ^3.8.0 cids: ^1.1.9 decimal.js: ^10.4.3 human-object-diff: ^3.0.0 @@ -4622,7 +4622,7 @@ __metadata: "@polkadot/api": "*" "@polkadot/types": "*" "@polkadot/util": "*" - checksum: 82f332edf3b4f7c711c88573d9d7013c265945520f0749a469e4101fd28480566da2acfd11ddf4359cde44f5fb061f310cacf298314db83e0fb9003008ffbbd5 + checksum: f088a6ea972f25574356eaa62ff7f78b0a4ebb1fcb8463dba51ca270e007ff6f6b16e3538119049e5c345f87baa81446096ab3be32f2eb977a5ce495ea328ea0 languageName: node linkType: hard @@ -4686,11 +4686,11 @@ __metadata: "@web3auth/openlogin-adapter": ^8.0.1 "@yornaath/batshit": ^0.8.0 "@yornaath/batshit-devtools-react": ^0.5.4 - "@zeitgeistpm/augment-api": 3.7.0 + "@zeitgeistpm/augment-api": 3.8.0 "@zeitgeistpm/avatara-nft-sdk": ^1.3.1 "@zeitgeistpm/avatara-util": ^1.2.0 - "@zeitgeistpm/sdk": 3.7.0 - "@zeitgeistpm/utility": 3.7.0 + "@zeitgeistpm/sdk": 3.8.0 + "@zeitgeistpm/utility": 3.8.0 autoprefixer: 10.2.5 axios: ^0.21.4 boring-avatars: ^1.6.1 @@ -4767,9 +4767,9 @@ __metadata: languageName: unknown linkType: soft -"@zeitgeistpm/utility@npm:3.7.0, @zeitgeistpm/utility@npm:^3.7.0": - version: 3.7.0 - resolution: "@zeitgeistpm/utility@npm:3.7.0" +"@zeitgeistpm/utility@npm:3.8.0, @zeitgeistpm/utility@npm:^3.8.0": + version: 3.8.0 + resolution: "@zeitgeistpm/utility@npm:3.8.0" dependencies: decimal.js: ^10.4.3 lodash.omit: ^4.5.0 @@ -4779,16 +4779,16 @@ __metadata: "@polkadot/api": "*" "@polkadot/types": "*" "@polkadot/util": "*" - checksum: b2a8cf5a4caa6e8b8bba8f45494278cd2424c6676691e750ee0294a01cd90683ea4c6c0a78d99a3450aa26e0889413771811410fc441a3113b2e226769b3be5d + checksum: eeb0a3bcf236422fc89e978c234cf13d0eac272158415df4ccb699a2fac2b708dab31caf11e30af3804a1afe8c2928287220940aab7ea82394db30dd3ccfe4e0 languageName: node linkType: hard -"@zeitgeistpm/web3.storage@npm:^3.7.0": - version: 3.7.0 - resolution: "@zeitgeistpm/web3.storage@npm:3.7.0" +"@zeitgeistpm/web3.storage@npm:^3.8.0": + version: 3.8.0 + resolution: "@zeitgeistpm/web3.storage@npm:3.8.0" dependencies: "@multiformats/sha3": ^3.0.2 - "@zeitgeistpm/utility": ^3.7.0 + "@zeitgeistpm/utility": ^3.8.0 cids: ^1.1.9 ipfs-http-client: ^60.0.0 ipfs-only-hash: ^4.0.0 @@ -4797,7 +4797,7 @@ __metadata: up: ^1.0.2 peerDependencies: "@polkadot/util": "*" - checksum: 220f412a6940298d985a0944fbe695e951ca48d36c5955eccc05af4cc2cae2240d60abcae7aaf36822e0cd2c30320085f5fa4a7af41de29d48d8bc6c160ace53 + checksum: fa43d2d3beccea21bd5d372accb21faca2187798b2bf58ad54f969a3e857de6149a2317107349c0b80f866da101d06986a22c03cbe10acebc4a17b6ad012e42a languageName: node linkType: hard