Skip to content

Commit

Permalink
Merge pull request #1845 from zeitgeistpm/tr-amm-2-trade-box
Browse files Browse the repository at this point in the history
AMM 2 trade box
  • Loading branch information
Robiquet authored Nov 14, 2023
2 parents 1b0783b + 566297c commit adea15b
Show file tree
Hide file tree
Showing 19 changed files with 1,064 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ NEXT_PUBLIC_AVATAR_COLLECTION_ID="2e55d4bf2e85715b63-ZEITASTAGE"
NEXT_PUBLIC_SINGULAR_URL="https://singular-rmrk2-dev.vercel.app"
NEXT_PUBLIC_RMRK_INDEXER_API="https://gql2.rmrk.dev/v1/graphql"
NEXT_PUBLIC_IPFS_NODE="http://ipfs.zeitgeist.pm:5001"
NEXT_PUBLIC_RMRK_CHAIN_RPC_NODE="wss://staging.node.rmrk.app"
NEXT_PUBLIC_RMRK_CHAIN_RPC_NODE="wss://kusama-node-staging.rmrk.link/"
NEXT_PUBLIC_AVATAR_API_HOST="https://avatar-bsr.zeitgeist.pm/"

#enable in dev/staging to inspect react-query cache and query handling.
Expand Down
26 changes: 24 additions & 2 deletions components/assets/AssetActionButtons/AssetTradingButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { Dialog } from "@headlessui/react";
import { ScalarAssetId, CategoricalAssetId } from "@zeitgeistpm/sdk";
import {
CategoricalAssetId,
ScalarAssetId,
getMarketIdOf,
} from "@zeitgeistpm/sdk";
import TradeForm from "components/trade-form";
import Amm2TradeForm from "components/trade-form/Amm2TradeForm";
import { TradeTabType } from "components/trade-form/TradeTab";
import Modal from "components/ui/Modal";
import SecondaryButton from "components/ui/SecondaryButton";
import { useMarket } from "lib/hooks/queries/useMarket";
import { useTradeItem } from "lib/hooks/trade";
import { useState } from "react";
import { ScoringRule } from "@zeitgeistpm/indexer";

const AssetTradingButtons = ({
assetId,
Expand All @@ -13,6 +21,8 @@ const AssetTradingButtons = ({
}) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const { data: tradeItem, set: setTradeItem } = useTradeItem();
const marketId = getMarketIdOf(assetId);
const { data: market } = useMarket({ marketId });

return (
<>
Expand Down Expand Up @@ -43,7 +53,19 @@ const AssetTradingButtons = ({
{tradeItem && (
<Modal open={isOpen} onClose={() => setIsOpen(false)}>
<Dialog.Panel className="w-full max-w-[462px] rounded-[10px] bg-white">
<TradeForm />
{market?.scoringRule === ScoringRule.Lmsr ? (
<Amm2TradeForm
marketId={marketId}
initialAsset={assetId}
selectedTab={
tradeItem.action === "buy"
? TradeTabType.Buy
: TradeTabType.Sell
}
/>
) : (
<TradeForm />
)}
</Dialog.Panel>
</Modal>
)}
Expand Down
17 changes: 8 additions & 9 deletions components/liquidity/MarketLiquiditySection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { formatScalarOutcome } from "lib/util/format-scalar-outcome";
import { perbillToNumber } from "lib/util/perbill-to-number";
import { FC, PropsWithChildren, useState } from "react";
import { AiOutlineInfoCircle } from "react-icons/ai";
import { ScoringRule } from "@zeitgeistpm/indexer";

export const MarketLiquiditySection = ({
market,
Expand All @@ -27,9 +28,13 @@ export const MarketLiquiditySection = ({
market: FullMarketFragment;
poll?: boolean;
}) => {
const marketHasPool =
(market?.scoringRule === ScoringRule.Cpmm && market.pool != null) ||
(market?.scoringRule === ScoringRule.Lmsr && market.neoPool != null);

return (
<>
{poll && !market?.pool?.poolId && (
{poll && !marketHasPool && (
<>
<div className="center">
<div className="h-12 w-12 center bg-white mr-4">
Expand All @@ -39,13 +44,13 @@ export const MarketLiquiditySection = ({
</div>
</>
)}
{market?.pool?.poolId && (
{marketHasPool && (
<>
<div className="mb-8">
<LiquidityHeader market={market} />
</div>
<PoolTable
poolId={market.pool.poolId}
poolId={market.pool?.poolId}
marketId={Number(market.marketId)}
/>
</>
Expand Down Expand Up @@ -151,12 +156,6 @@ const LiquidityHeader = ({ market }: { market: FullMarketFragment }) => {
</div>
</InfoPopover>
</LiquidityHeaderTextItem>
<LiquidityHeaderTextItem
label="Prediction"
className="border-b-1 sm:border-b-0 md:border-r-1"
>
{predictionDisplay}
</LiquidityHeaderTextItem>
</div>
<div className="flex md:w-full">
{!wallet.connected ? (
Expand Down
24 changes: 11 additions & 13 deletions components/liquidity/PoolTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IOBaseAssetId, parseAssetId, ZTG } from "@zeitgeistpm/sdk";
import Table, { TableColumn, TableData } from "components/ui/Table";
import Decimal from "decimal.js";
import { useAmm2Pool } from "lib/hooks/queries/amm2/useAmm2Pool";
import { useAccountPoolAssetBalances } from "lib/hooks/queries/useAccountPoolAssetBalances";
import { useAssetMetadata } from "lib/hooks/queries/useAssetMetadata";
import { useAssetUsdPrice } from "lib/hooks/queries/useAssetUsdPrice";
Expand All @@ -10,18 +11,14 @@ import { usePool } from "lib/hooks/queries/usePool";
import { usePoolBaseBalance } from "lib/hooks/queries/usePoolBaseBalance";
import { calcMarketColors } from "lib/util/color-calc";
import { parseAssetIdString } from "lib/util/parse-asset-id";
import { ScoringRule } from "@zeitgeistpm/indexer";

const poolTableColums: TableColumn[] = [
{
header: "Token",
accessor: "token",
type: "token",
},
{
header: "Weights",
accessor: "weights",
type: "percentage",
},
{
header: "Pool Balance",
accessor: "poolBalance",
Expand All @@ -33,10 +30,10 @@ const PoolTable = ({
poolId,
marketId,
}: {
poolId: number;
poolId?: number;
marketId: number;
}) => {
const { data: pool } = usePool({ poolId });
const { data: pool } = usePool(poolId != null ? { poolId } : undefined);
const { data: market } = useMarket({ marketId });
const baseAssetId = pool?.baseAsset
? parseAssetId(pool.baseAsset).unrightOr(undefined)
Expand All @@ -50,20 +47,25 @@ const PoolTable = ({
const { data: basePoolBalance } = usePoolBaseBalance(poolId);
const { data: baseAssetUsdPrice } = useAssetUsdPrice(baseAssetId);
const { data: spotPrices } = useMarketSpotPrices(marketId);
const { data: amm2Pool } = useAmm2Pool(marketId);

const colors = market?.categories
? calcMarketColors(marketId, market.categories.length)
: [];

const assetIds =
market?.scoringRule === ScoringRule.Cpmm
? pool?.weights?.map((weight) => parseAssetIdString(weight?.assetId))
: amm2Pool?.assetIds;

const tableData: TableData[] =
pool?.weights?.map((asset, index) => {
assetIds?.map((assetId, index) => {
let amount: Decimal | undefined;
let usdValue: Decimal | undefined;
let category:
| { color?: string | null; name?: string | null }
| undefined
| null;
const assetId = parseAssetIdString(asset?.assetId);

if (IOBaseAssetId.is(assetId)) {
amount = basePoolBalance ?? undefined;
Expand All @@ -83,10 +85,6 @@ const PoolTable = ({
color: colors[index] || "#ffffff",
label: category?.name ?? "",
},
weights: new Decimal(asset!.weight)
.div(pool.totalWeight)
.mul(100)
.toNumber(),
poolBalance: {
value: amount?.div(ZTG).toDecimalPlaces(2).toNumber() ?? 0,
usdValue: usdValue?.div(ZTG).toDecimalPlaces(2).toNumber(),
Expand Down
6 changes: 3 additions & 3 deletions components/markets/MarketContextActionOutcomeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type MarketContextActionOutcomeSelectorProps = {

const SEARCH_ITEMS_THRESHOLD = 5;

export const MarketContextActionOutcomeSelector = ({
const MarketContextActionOutcomeSelector = ({
market,
selected,
options,
Expand Down Expand Up @@ -88,14 +88,14 @@ export const MarketContextActionOutcomeSelector = ({
}}
>
<Listbox.Button onClick={() => setOpen(!open)}>
<div className="center gap-2 text-2xl md:text-xl lg:text-2xl">
<div className="center gap-2 md:text-lg lg:text-xl text-ztg-blue">
<TruncatedText
length={24}
text={market.categories?.[getIndexOf(selected)].name ?? ""}
>
{(text) => <>{text}</>}
</TruncatedText>
<RiArrowDownSLine />
<RiArrowDownSLine className="text-black" />
</div>
</Listbox.Button>
<Transition
Expand Down
12 changes: 6 additions & 6 deletions components/markets/TradeResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export const TwitterBird = () => {

interface TradeResultProps {
type: "buy" | "sell";
amount: Decimal;
amount?: Decimal;
tokenName?: string;
baseTokenAmount: Decimal;
baseTokenAmount?: Decimal;
baseToken?: string;
marketId: number;
marketQuestion?: string;
Expand All @@ -49,12 +49,12 @@ const TradeResult = ({
onContinueClick,
}: TradeResultProps) => {
const marketUrl = `https://app.zeitgeist.pm/markets/${marketId}`;
const potentialGain = amount.div(baseTokenAmount);
const potentialGain = amount?.div(baseTokenAmount ?? 0);
const twitterBaseUrl = "https://twitter.com/intent/tweet?text=";
const tweetUrl =
type === "buy"
? `${twitterBaseUrl}I'm using %40ZeitgeistPM to bet on "${marketQuestion}" %0A%0AIf I'm right, I'll gain ${potentialGain
.minus(1)
?.minus(1)
.times(100)
.toFixed(
0,
Expand All @@ -64,12 +64,12 @@ const TradeResult = ({
return (
<div className="flex flex-col items-center gap-y-[10px] rounded-ztg-10 bg-white p-[30px] text-ztg-18-150">
<div>You've just {type === "buy" ? "bought" : "sold"}</div>
<div className="text-[58px]">{amount.toFixed(2)}</div>
<div className="text-[58px]">{amount?.toFixed(2)}</div>
<div className="text-center">
<span className="font-bold capitalize">{tokenName}</span> Predictions
For
<div className="font-bold">
{baseTokenAmount.toFixed(2)} {baseToken}
{baseTokenAmount?.toFixed(2)} {baseToken}
</div>
</div>
<a
Expand Down
140 changes: 140 additions & 0 deletions components/trade-form/Amm2TradeForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { Tab } from "@headlessui/react";
import { MarketOutcomeAssetId, getIndexOf, ZTG } from "@zeitgeistpm/sdk";
import { useEffect, useState } from "react";
import BuyForm from "./BuyForm";
import SellForm from "./SellForm";
import TradeTab, { TradeTabType } from "./TradeTab";
import { ISubmittableResult } from "@polkadot/types/types";
import TradeResult from "components/markets/TradeResult";
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";

const Amm2TradeForm = ({
marketId,
selectedTab,
initialAsset,
showTabs = true,
}: {
marketId: number;
selectedTab?: TradeTabType;
initialAsset?: MarketOutcomeAssetId;
showTabs?: boolean;
}) => {
const [tabType, setTabType] = useState<TradeTabType>();
const [showSuccessBox, setShowSuccessBox] = useState(false);
const [amountReceived, setAmountReceived] = useState<Decimal>();
const [amountIn, setAmountIn] = useState<Decimal>();
const [outcomeAsset, setOutcomeAsset] = useState<MarketOutcomeAssetId>();
const { data: market } = useMarket({ marketId });
const baseAsset = parseAssetIdString(market?.baseAsset);
const { data: assetMetadata } = useAssetMetadata(baseAsset);
const baseSymbol = assetMetadata?.symbol;

useEffect(() => {
setTabType(selectedTab ?? TradeTabType.Buy);
}, [selectedTab]);

const handleSuccess = (data: ISubmittableResult) => {
const { events } = data;
for (const eventData of events) {
const { event } = eventData;
const { data } = event;
if (
event.section.toString() === "neoSwaps" &&
(event.method.toString() === "SellExecuted" ||
event.method.toString() === "BuyExecuted")
) {
const amountOut: number = data["amountOut"].toNumber();
setAmountReceived(new Decimal(amountOut ?? 0));
setShowSuccessBox(true);
}
}
};

return (
<>
{showSuccessBox === true ? (
<TradeResult
type={tabType === TradeTabType.Buy ? "buy" : "sell"}
amount={
tabType === TradeTabType.Buy
? amountReceived?.div(ZTG)
: amountIn?.div(ZTG)
}
tokenName={
outcomeAsset && market?.categories
? market.categories[getIndexOf(outcomeAsset)].name ?? ""
: ""
}
baseTokenAmount={
tabType === TradeTabType.Buy
? amountIn?.div(ZTG)
: amountReceived?.div(ZTG)
}
baseToken={baseSymbol}
marketId={marketId}
marketQuestion={market?.question ?? ""}
onContinueClick={() => {
setShowSuccessBox(false);
}}
/>
) : (
<Tab.Group
onChange={(index: TradeTabType) => {
setTabType(index);
}}
selectedIndex={tabType}
>
<Tab.List
className={`h-[71px] text-center font-medium text-ztg-18-150 ${
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]"
>
Sell
</Tab>
</Tab.List>
<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>
</Tab.Panels>
</Tab.Group>
)}
</>
);
};

export default Amm2TradeForm;
Loading

0 comments on commit adea15b

Please sign in to comment.