Skip to content

Commit

Permalink
Merge pull request #2445 from zeitgeistpm/staging
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Jun 19, 2024
2 parents cf12ef1 + 58abe00 commit 07b1b61
Show file tree
Hide file tree
Showing 18 changed files with 1,392 additions and 151 deletions.
152 changes: 152 additions & 0 deletions components/orderbook/OrdersTable.tsx
Original file line number Diff line number Diff line change
@@ -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<OrderWhereInput> }) => {
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: (
<CancelOrderButton
orderId={id}
disabled={
realAddress !== makerAddress ||
orderFilled ||
status === OrderStatus.Removed
}
/>
),
};
},
);
return (
<div>
<Table columns={columns} data={tableData} showHighlight={false} />
</div>
);
};

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 (
<SecondaryButton
onClick={() => cancelOrder()}
disabled={isLoading || isSuccess || disabled}
>
Cancel Order
</SecondaryButton>
);
};

export default OrdersTable;
183 changes: 141 additions & 42 deletions components/trade-form/Amm2TradeForm.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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,
Expand All @@ -23,6 +28,7 @@ const Amm2TradeForm = ({
showTabs?: boolean;
}) => {
const [tabType, setTabType] = useState<TradeTabType>();
const [orderType, setOrderType] = useState<OrderType>("market");
const [showSuccessBox, setShowSuccessBox] = useState(false);
const [amountReceived, setAmountReceived] = useState<Decimal>();
const [amountIn, setAmountIn] = useState<Decimal>();
Expand Down Expand Up @@ -87,54 +93,147 @@ const Amm2TradeForm = ({
}}
selectedIndex={tabType}
>
<Tab.List
className={`h-[71px] text-center text-ztg-18-150 font-medium ${
showTabs ? "flex" : "hidden"
}`}
>
<Tab
as={TradeTab}
selected={tabType === TradeTabType.Buy}
className="rounded-tl-[10px]"
>
Buy
</Tab>
<Tab
as={TradeTab}
selected={tabType === TradeTabType.Sell}
className="rounded-tr-[10px]"
<div className="flex">
<Tab.List
className={`h-[51px] w-[75%] text-center text-ztg-18-150 font-medium ${
showTabs ? "flex" : "hidden"
}`}
>
Sell
</Tab>
</Tab.List>
<Tab
as={TradeTab}
selected={tabType === TradeTabType.Buy}
className="rounded-tl-[10px]"
>
Buy
</Tab>
<Tab
as={TradeTab}
selected={tabType === TradeTabType.Sell}
className="rounded-tr-[10px]"
>
Sell
</Tab>
</Tab.List>
<OrderTypeSelector
onTypeSelected={(type) => {
setOrderType(type);
}}
value={orderType}
/>
</div>
<Tab.Panels className="p-[30px]">
<Tab.Panel>
<BuyForm
marketId={marketId}
initialAsset={initialAsset}
onSuccess={(data, asset, amount) => {
handleSuccess(data);
setOutcomeAsset(asset);
setAmountIn(amount);
}}
/>
</Tab.Panel>
<Tab.Panel>
<SellForm
marketId={marketId}
initialAsset={initialAsset}
onSuccess={(data, asset, amount) => {
handleSuccess(data);
setOutcomeAsset(asset);
setAmountIn(amount);
}}
/>
</Tab.Panel>
{orderType === "market" ? (
<>
<Tab.Panel>
<BuyForm
marketId={marketId}
initialAsset={initialAsset}
onSuccess={(data, asset, amount) => {
handleSuccess(data);
setOutcomeAsset(asset);
setAmountIn(amount);
}}
/>
</Tab.Panel>
<Tab.Panel>
<SellForm
marketId={marketId}
initialAsset={initialAsset}
onSuccess={(data, asset, amount) => {
handleSuccess(data);
setOutcomeAsset(asset);
setAmountIn(amount);
}}
/>
</Tab.Panel>
</>
) : (
<>
<Tab.Panel>
<LimitBuyOrderForm
marketId={marketId}
initialAsset={initialAsset}
/>
</Tab.Panel>
<Tab.Panel>
<LimitSellOrderForm
marketId={marketId}
initialAsset={initialAsset}
/>
</Tab.Panel>
</>
)}
</Tab.Panels>
</Tab.Group>
)}
</>
);
};

type OrderType = "market" | "limit";

const OrderTypeSelector = ({
onTypeSelected,
value,
}: {
onTypeSelected: (type: OrderType) => void;
value: OrderType;
}) => {
const [menuOpen, setMenuOpen] = useState(false);
const wrapperRef = useRef<HTMLDivElement>(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 (
<div className="relative flex w-[25%] items-center justify-center">
<button
onClick={() => setMenuOpen((open) => !open)}
className="flex w-full items-center justify-center px-5"
>
<div>{value === "market" ? "Market" : "Limit"}</div>
<ChevronDown className="ml-auto" size={16} />
</button>

{menuOpen && (
<div
ref={wrapperRef}
className="absolute top-[52px] flex w-32 flex-col gap-y-3 rounded-lg bg-white p-4 shadow-[0px_4px_20px_0px_#00000040]"
>
<button
className={`${
value === "market" ? "font-medium text-black" : "text-sky-600"
} `}
onClick={() => handleTypeClick("market")}
>
Market
</button>
<button
className={`${
value === "limit" ? "font-medium text-black" : "text-sky-600"
} `}
onClick={() => handleTypeClick("limit")}
>
Limit
</button>
</div>
)}
</div>
);
};

export default Amm2TradeForm;
Loading

0 comments on commit 07b1b61

Please sign in to comment.