diff --git a/package-lock.json b/package-lock.json
index 2c2e51688..6ce885c4e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7088,6 +7088,10 @@
"resolved": "packages/thena",
"link": true
},
+ "node_modules/@orbs-network/twap-ui-tradingpost": {
+ "resolved": "packages/tradingpost",
+ "link": true
+ },
"node_modules/@parcel/watcher": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz",
@@ -36647,6 +36651,56 @@
"react": "*",
"react-dom": "*"
}
+ },
+ "packages/tradingpost": {
+ "version": "1.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "@defi.org/web3-candies": "^4.20",
+ "@mui/material": "5.x",
+ "@mui/system": "5.x",
+ "@orbs-network/twap": "^1.18.x",
+ "@orbs-network/twap-ui": "^0.10.14",
+ "lodash": "4.x",
+ "web3": "1.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "packages/tradingpost/node_modules/@orbs-network/twap-ui": {
+ "version": "0.10.23",
+ "resolved": "https://registry.npmjs.org/@orbs-network/twap-ui/-/twap-ui-0.10.23.tgz",
+ "integrity": "sha512-gx/kpl71eTdA0tTzqBFyFSzcvUGV3IeuPJjiFFw+fuesTzdnPvs3Pg4cGMN+wlI4siLi+eSm/vw0n8IapYDPug==",
+ "dependencies": {
+ "@defi.org/web3-candies": "^4.20",
+ "@emotion/react": "11.x",
+ "@emotion/styled": "11.x",
+ "@mui/material": "5.x",
+ "@mui/system": "5.x",
+ "@orbs-network/twap": "^1.18.x",
+ "@react-icons/all-files": "^4.1.0",
+ "@tanstack/react-query": "4.x",
+ "@types/async-retry": "^1.4.5",
+ "@types/lodash": "4.x",
+ "async-retry": "^1.3.3",
+ "bignumber.js": "9.x",
+ "emotion-theming": "11.x",
+ "isomorphic-fetch": "3.x",
+ "lodash": "4.x",
+ "moment": "2.x",
+ "qrcode.react": "^3.1.0",
+ "react-error-boundary": "^4.0.10",
+ "react-number-format": "5.x",
+ "react-text-overflow": "^1.0.2",
+ "web3": "1.x",
+ "zustand": "4.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
}
}
}
diff --git a/packages/dapp-example/src/TradingPost.tsx b/packages/dapp-example/src/TradingPost.tsx
new file mode 100644
index 000000000..9b75151e5
--- /dev/null
+++ b/packages/dapp-example/src/TradingPost.tsx
@@ -0,0 +1,305 @@
+import { StyledModalContent, StyledTradingPost, StyledPancakeBackdrop, StyledTradingPostLayout, StyledPancakeOrders, StyledPancakeTwap } from "./styles";
+import { TWAP, Orders, parseToken } from "@orbs-network/twap-ui-tradingpost";
+import { useConnectWallet, useGetTokens, useIsMobile, usePriceUSD, useTheme, useTrade } from "./hooks";
+import { Configs } from "@orbs-network/twap";
+import { useWeb3React } from "@web3-react/core";
+import { Dapp, TokensList, UISelector } from "./Components";
+import { Popup } from "./Components";
+import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
+import _ from "lodash";
+import { erc20s, isNativeAddress, zeroAddress } from "@defi.org/web3-candies";
+import { SelectorOption, TokenListItem } from "./types";
+import { Box } from "@mui/system";
+import { Button, styled, Tooltip, Typography } from "@mui/material";
+import { Components, hooks, Styles } from "@orbs-network/twap-ui";
+import BN from "bignumber.js";
+const config = Configs.PancakeSwap;
+
+const tradingPostConfig = {
+ ...config,
+ name: "TradingPost",
+};
+
+let native = {
+ ...tradingPostConfig.nativeToken,
+ logoURI: tradingPostConfig.nativeToken.logoUrl,
+};
+
+const parseListToken = (tokenList: any) => {
+ const result = tokenList.tokens.map(({ symbol, address, decimals, logoURI, name }: any) => ({
+ decimals,
+ symbol,
+ name,
+ address,
+ logoURI: logoURI.replace("_1", ""),
+ }));
+
+ return [native, ...result];
+};
+export const useDappTokens = () => {
+ return useGetTokens({
+ chainId: config.chainId,
+ parse: parseListToken,
+ modifyList: (tokens: any) => ({ ..._.mapKeys(tokens, (t) => t.address) }),
+ baseAssets: erc20s.bsc,
+ url: `https://tokens.pancakeswap.finance/pancakeswap-extended.json`,
+ });
+};
+
+interface TokenSelectModalProps {
+ onCurrencySelect: (value: any) => void;
+ selectedCurrency?: any;
+ otherSelectedCurrency?: any;
+}
+
+const parseList = (rawList?: any): TokenListItem[] => {
+ return _.map(rawList, (rawToken) => {
+ return {
+ token: {
+ address: rawToken.address,
+ decimals: rawToken.decimals,
+ symbol: rawToken.symbol,
+ logoUrl: rawToken.logoURI,
+ },
+ rawToken,
+ };
+ });
+};
+
+const ConnectButton = () => {
+ const connect = useConnectWallet();
+ return (
+
+
+
+ );
+};
+
+interface ContextProps {
+ openModal: (value: boolean) => void;
+ close: () => void;
+ showModal?: boolean;
+ isFrom?: boolean;
+ setIsFrom?: (value: boolean) => void;
+}
+const Context = createContext({} as ContextProps);
+
+const ContextWrapper = ({ children }: { children: ReactNode }) => {
+ const [showModal, setShowModal] = useState(false);
+ const [isFrom, setIsFrom] = useState(true);
+
+ const openModal = (value: boolean) => {
+ setIsFrom(value);
+ setShowModal(true);
+ };
+
+ return setShowModal(false) }}>{children};
+};
+
+const TokenSelectModal = ({ onCurrencySelect }: TokenSelectModalProps) => {
+ const { data: dappTokens } = useDappTokens();
+
+ const tokensListSize = _.size(dappTokens);
+ const parsedList = useMemo(() => parseList(dappTokens), [tokensListSize]);
+
+ return (
+
+
+
+ );
+};
+
+const useDecimals = (fromToken?: string, toToken?: string) => {
+ const { data: dappTokens } = useDappTokens();
+ const fromTokenDecimals = dappTokens?.[fromToken || ""]?.decimals;
+ const toTokenDecimals = dappTokens?.[toToken || ""]?.decimals;
+
+ return { fromTokenDecimals, toTokenDecimals };
+};
+
+const handleAddress = (address?: string) => {
+ return !address ? "" : "BNB" ? zeroAddress : address;
+};
+
+const useTokenModal = (item1: any, item2: any, item3: any, isFrom?: boolean) => {
+ const context = useContext(Context);
+ return () => context.openModal(!!isFrom);
+};
+
+const useTooltip = (content: ReactNode, options?: any, children?: ReactNode) => {
+ const targetRef = useRef(null);
+
+ const tooltip = (
+
+ {children}
+
+ );
+
+ return {
+ targetRef,
+ tooltip,
+ };
+};
+
+const DappButton = ({ isLoading, disabled, children, onClick }: any) => {
+ return (
+
+ {children}
+
+ );
+};
+
+const StyledButton = styled(Button)({
+ width: "100%",
+});
+
+const ApproveModalContent = ({ title, isBonus, isMM }: { title: string; isBonus: boolean; isMM: boolean }) => {
+ return Approving
;
+};
+
+const SwapTransactionErrorContent = ({ message }: { message: string }) => {
+ return {message}
;
+};
+const SwapPendingModalContent = ({ title }: { title: string }) => {
+ return {title}
;
+};
+
+const TWAPComponent = ({ limit }: { limit?: boolean }) => {
+ const { isDarkTheme } = useTheme();
+ const { account, library, chainId } = useWeb3React();
+ const { data: dappTokens } = useDappTokens();
+ const isMobile = useIsMobile();
+
+ const _useTrade = (fromToken?: string, toToken?: string, amount?: string) => {
+ const { fromTokenDecimals, toTokenDecimals } = useDecimals(handleAddress(fromToken), handleAddress(toToken));
+ return useTrade(fromToken, toToken, amount, fromTokenDecimals, toTokenDecimals);
+ };
+
+ const connector = useMemo(() => {
+ return {
+ getProvider: () => library,
+ };
+ }, [library]);
+
+ return (
+
+ console.log(it)}
+ nativeToken={native}
+ connector={connector}
+ isMobile={isMobile}
+ useTooltip={useTooltip}
+ Button={DappButton}
+ ApproveModalContent={ApproveModalContent}
+ SwapTransactionErrorContent={SwapTransactionErrorContent}
+ SwapPendingModalContent={SwapPendingModalContent}
+ SwapTransactionReceiptModalContent={SwapPendingModalContent}
+ TradePrice={TradePrice}
+ TradePriceToggle={TradePriceToggle}
+ />
+
+ );
+};
+
+const logo = "https://avatars.githubusercontent.com/u/170947423?s=200&v=4";
+const DappComponent = () => {
+ const { isDarkTheme } = useTheme();
+ const [selected, setSelected] = useState(SelectorOption.TWAP);
+ const isMobile = useIsMobile();
+
+ return (
+
+
+
+ {isMobile && (
+
+
+
+ )}
+
+
+
+
+
+
+ {!isMobile && (
+
+
+
+ )}
+
+
+ );
+};
+
+const Tokens = () => {
+ const context = useContext(Context);
+
+ const selectToken = hooks.useSelectTokenCallback(parseToken);
+
+ const onSelect = useCallback(
+ (token: any) => {
+ selectToken({ isSrc: !!context.isFrom, token });
+ context.close();
+ },
+ [selectToken, context.isFrom, context.close]
+ );
+
+ return (
+
+ ;
+
+ );
+};
+const Wrapper = ({ children, className = "" }: { children: ReactNode; className?: string }) => {
+ const { isDarkTheme } = useTheme();
+
+ return (
+
+
+ {children}
+
+ );
+};
+
+const StyledWrapper = styled(Box)({
+ position: "relative",
+ width: "100%",
+});
+
+const dapp: Dapp = {
+ Component: DappComponent,
+ logo,
+ config: tradingPostConfig,
+};
+
+export default dapp;
+
+const TradePriceToggle = ({ onClick }: { onClick: () => void }) => {
+ return ;
+};
+
+const TradePrice = (props: { leftSymbol?: string; rightSymbol?: string; price?: string }) => {
+ return (
+
+ 1 {props.leftSymbol} = {props.price} {props.rightSymbol}
+
+ );
+};
+
+export const amountUi = (decimals?: number, amount?: BN) => {
+ if (!decimals || !amount) return "";
+ const percision = BN(10).pow(decimals || 0);
+ return amount.times(percision).idiv(percision).div(percision).toString();
+};
diff --git a/packages/dapp-example/src/config.ts b/packages/dapp-example/src/config.ts
index fa649fecb..f380b1ec7 100644
--- a/packages/dapp-example/src/config.ts
+++ b/packages/dapp-example/src/config.ts
@@ -12,6 +12,24 @@ import lynex from "./Lynex";
import arbidex from "./Arbidex";
import syncswap from "./SyncSwap";
import kinetix from "./kinetix";
+import tradingpost from "./TradingPost";
-export const defaultDapp = quickswap;
-export const dapps = [quickswap, spookyswap, spiritswap, pangolin, pangolinDaas, chronos, thena, baseswap, arbidex, lynex, stellaswap, pancake, sushiswap, syncswap, kinetix];
+export const defaultDapp = tradingpost;
+export const dapps = [
+ quickswap,
+ spookyswap,
+ spiritswap,
+ pangolin,
+ pangolinDaas,
+ chronos,
+ thena,
+ baseswap,
+ arbidex,
+ lynex,
+ stellaswap,
+ pancake,
+ tradingpost,
+ sushiswap,
+ syncswap,
+ kinetix,
+];
diff --git a/packages/dapp-example/src/styles.ts b/packages/dapp-example/src/styles.ts
index ced6cb900..bcc40820b 100644
--- a/packages/dapp-example/src/styles.ts
+++ b/packages/dapp-example/src/styles.ts
@@ -134,6 +134,25 @@ export const StyledPancake = styled(StyledDapp)<{ isDarkTheme: number }>(({ isDa
},
}));
+export const StyledTradingPost = styled(StyledDapp)<{ isDarkTheme: number }>(({ isDarkTheme }) => ({
+ background: isDarkTheme ? "#131311" : "#F7E4D4",
+ ".ui-selector-btn": {
+ background: "#1fc7d4",
+ color: isDarkTheme ? "white" : "black",
+ },
+ ".ui-selector-btn-selected": {
+ background: "#7a6eaa",
+ color: "white",
+ },
+ ".menu-button": {
+ svg: {
+ "* ": {
+ color: isDarkTheme ? "#FBFBFB" : "#000315",
+ },
+ },
+ },
+}));
+
export const StyledStella = styled(StyledDapp)<{ isDarkMode: number }>(({ isDarkMode }) => ({
background: isDarkMode ? "#251842" : "#F4F5F6",
".ui-selector-btn": {
@@ -421,7 +440,7 @@ export const StyledThenaLayout = styled(DappLayout)({
});
export const StyledPancakeTwap = styled(Box)<{ isDarkTheme: number }>(({ isDarkTheme }) => ({
- background: isDarkTheme ? "#27262C" : "#FFFFFF",
+ background: isDarkTheme ? "#27262C" : "#F7E4D4",
padding: 16,
borderRadius: 24,
position: "relative",
@@ -458,6 +477,15 @@ export const StyledPancakeLayout = styled(DappLayout)({
maxWidth: 326,
});
+export const StyledTradingPostLayout = styled(DappLayout)({
+ width: "calc(100% - 30px)",
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ position: "relative",
+ maxWidth: 540,
+});
+
export const StyledSushiLayout = styled(DappLayout)({
maxWidth: 490,
width: "calc(100% - 30px)",
diff --git a/packages/lib/src/components/Labels.tsx b/packages/lib/src/components/Labels.tsx
index 1449d166f..345d997e5 100644
--- a/packages/lib/src/components/Labels.tsx
+++ b/packages/lib/src/components/Labels.tsx
@@ -118,3 +118,18 @@ export const OrdersLabel = ({ className = "" }: { className?: string }) => {
);
};
+
+export const PartDurationLabel = () => {
+ const translations = useTwapContext().translations;
+ return {translations.partDuration};
+}
+
+export const TotalDuarationLabel = () => {
+ const translations = useTwapContext().translations;
+ return {translations.totalDuration};
+}
+
+export const TradingPostLimitPriceLabel = () => {
+ const translations = useTwapContext().translations;
+ return {translations.limitPrice};
+}
diff --git a/packages/lib/src/components/OrdersComponents.tsx b/packages/lib/src/components/OrdersComponents.tsx
index a3b6f5905..0ef69f80a 100644
--- a/packages/lib/src/components/OrdersComponents.tsx
+++ b/packages/lib/src/components/OrdersComponents.tsx
@@ -6,6 +6,7 @@ import { useOrdersHistoryQuery, useOrdersTabs } from "../hooks";
import { useOrdersStore } from "../store";
import { StyledOrdersLists, StyledOrdersTab, StyledOrdersTabs } from "../styles";
import OrdersList from "../orders/OrdersList";
+import { Dex } from "../consts";
function a11yProps(index: number) {
return {
@@ -39,7 +40,26 @@ export const OrdersSelectTabs = ({ className = "" }: { className?: string }) =>
);
};
-export const SelectedOrders = ({ className = "" }: { className?: string }) => {
+/**
+ * The `SelectedOrders` component renders a list of `OrdersList` components based on the selected tab
+ * and the specified decentralized exchange (DEX).
+ *
+ * This component dynamically selects and renders the appropriate `OrdersList` based on the selected
+ * tab and the `dex` prop. If the `dex` is `TradingPost`, a specific `OrdersList` with customized
+ * props is rendered.
+ *
+ * @param {string} [className=""] - Optional additional class names for styling the component.
+ * @param {Dex} [dex] - The decentralized exchange (DEX) being used. This prop controls the behavior
+ * of the component. The available options for `dex` are:
+ * - `Dex.Uniswap` ("uniswap")
+ * - `Dex.Sushiswap` ("sushiswap")
+ * - `Dex.Quickswap` ("quickswap")
+ * - `Dex.TradingPost` ("tradingpost")
+ *
+ * @returns {JSX.Element} A list of `OrdersList` components rendered based on the selected tab value
+ * and the specified `dex`.
+ */
+export const SelectedOrders = ({ className = "", dex }: { className?: string, dex?: string }) => {
const { orders, isLoading } = useOrdersHistoryQuery();
const { tab } = useOrdersStore();
const tabs = useOrdersTabs();
@@ -54,7 +74,9 @@ export const SelectedOrders = ({ className = "" }: { className?: string }) => {
if (tabValue === "All") {
return ;
}
-
+ if (dex === Dex.TradingPost) {
+ return ;
+ }
return ;
})}
diff --git a/packages/lib/src/consts.ts b/packages/lib/src/consts.ts
index 5887134aa..4d4aeeec9 100644
--- a/packages/lib/src/consts.ts
+++ b/packages/lib/src/consts.ts
@@ -17,3 +17,10 @@ export const QUERY_PARAMS = {
};
export const SUGGEST_CHUNK_VALUE = 100;
+
+export enum Dex {
+ Uniswap = "uniswap",
+ Sushiswap = "sushiswap",
+ Quickswap = "quickswap",
+ TradingPost = "tradingpost",
+}
diff --git a/packages/lib/src/hooks.ts b/packages/lib/src/hooks.ts
index a45501712..bf6d1e93f 100644
--- a/packages/lib/src/hooks.ts
+++ b/packages/lib/src/hooks.ts
@@ -1367,8 +1367,8 @@ export const useLimitPriceV2 = () => {
original: BN(original || "0").isZero() ? "" : original,
};
}, [marketPrice, enableQueryParams, limitPriceStore.inverted, limitPriceStore.limitPrice, limitPriceStore.isCustom, limitPriceStore.priceFromQueryParams]);
- console.log({limitPrice});
-
+ console.log({ limitPrice });
+
const onInvert = useCallback(() => {
limitPriceStore.toggleInverted();
}, [limitPriceStore.toggleInverted, limitPrice]);
diff --git a/packages/lib/src/i18n/en.json b/packages/lib/src/i18n/en.json
index 2d2952494..41df6ea4c 100644
--- a/packages/lib/src/i18n/en.json
+++ b/packages/lib/src/i18n/en.json
@@ -47,6 +47,8 @@
"tradeSizeMustBeEqual": "Trade size must be equal to at least 10 USD",
"tradeSize": "Trade size",
"tradeInterval": "Trade interval",
+ "partDuration": "Part Duration",
+ "totalDuration": "Total Duration",
"maxDuration": "Max duration",
"totalTrades": "Total trades",
"deadline": "Deadline",
@@ -68,11 +70,13 @@
"Expired": "Expired",
"Canceled": "Canceled",
"noOrdersFound": "You currently don't have",
+ "noOrdersFound_Swap": "Currently, there are no",
"noOrdersFound_Open": "open",
"noOrdersFound_Completed": "completed",
"noOrdersFound_Expired": "expired",
"noOrdersFound_Canceled": "canceled",
"noOrdersFound1": "orders",
+ "noOrdersFound2": "create a new one !",
"confirmTx": "Confirm Transaction",
"expiration": "Expiration",
"orderType": "Order type",
diff --git a/packages/lib/src/orders/OrdersList.tsx b/packages/lib/src/orders/OrdersList.tsx
index 85ddb416c..640d6d88a 100644
--- a/packages/lib/src/orders/OrdersList.tsx
+++ b/packages/lib/src/orders/OrdersList.tsx
@@ -1,4 +1,4 @@
-import { Box, styled } from "@mui/material";
+import { Box, styled, Typography, Button } from "@mui/material";
import { useState } from "react";
import Order, { OrderLoader } from "./Order/Order";
import CircularProgress from "@mui/material/CircularProgress";
@@ -9,8 +9,11 @@ import { usePagination } from "../hooks";
import { StyledColumnFlex } from "../styles";
import { Pagination } from "../components/base";
import { useTwapStore } from "../store";
+import Bear from "./assets/components/Bear";
+import ArrowOutward from "./assets/components/ArrowOutward";
+import { Dex } from "../consts";
-function OrdersList({ orders, status, isLoading }: { orders?: ParsedOrder[]; status?: string; isLoading: boolean }) {
+function OrdersList({ orders, status, isLoading, dex }: { orders?: ParsedOrder[]; status?: string; isLoading: boolean, dex?: Dex }) {
const { uiPreferences } = useTwapContext();
const showPagination = uiPreferences.orders?.paginationChunks && _.size(orders) > uiPreferences.orders?.paginationChunks;
@@ -25,7 +28,13 @@ function OrdersList({ orders, status, isLoading }: { orders?: ParsedOrder[]; sta
if (showPagination) {
return ;
}
- return
;
+
+ switch (dex) {
+ case Dex.TradingPost:
+ return ;
+ default:
+ return
;
+ }
}
const PaginationList = ({ orders, status }: { orders?: ParsedOrder[]; status?: string }) => {
@@ -41,7 +50,7 @@ const PaginationList = ({ orders, status }: { orders?: ParsedOrder[]; status?: s
);
};
-const List = ({ orders, status }: { orders?: ParsedOrder[]; status?: string }) => {
+const List = ({ orders, status, useCustomComponent, CustomComponent }: { orders?: ParsedOrder[]; status?: string; useCustomComponent?: boolean; CustomComponent?: (props: { status?: string }) => React.ReactElement; }) => {
const [selected, setSelected] = useState(undefined);
const { translations } = useTwapContext();
const waitingForOrdersUpdate = useTwapStore((s) => s.waitingForOrdersUpdate);
@@ -53,6 +62,12 @@ const List = ({ orders, status }: { orders?: ParsedOrder[]; status?: string }) =
if (!_.size(orders)) {
return waitingForOrdersUpdate ? (
+ ) : useCustomComponent ? (
+
+
+ {CustomComponent && }
+
+
) : (
@@ -72,6 +87,21 @@ const List = ({ orders, status }: { orders?: ParsedOrder[]; status?: string }) =
);
};
+const TradingPostListComponent = ({status}: {status?: string}) => {
+ const { translations } = useTwapContext();
+ return (
+ <>
+
+ No Open Orders
+ {!status ? "You currently don't have orders" : `${translations.noOrdersFound_Swap} ${(translations as any)["noOrdersFound_" + status]} ${translations.noOrdersFound1}`}
+
+ Learn More
+
+
+ >
+ );
+}
+
export default OrdersList;
const StyledEmptyList = styled(Box)({
@@ -96,3 +126,23 @@ const StyledLoader = styled(StyledContainer)({
color: "white",
},
});
+
+const StyledEmtpyListTitle = styled(Typography)({
+ fontSize: 18,
+ fontWeight: 700,
+ fontFamily: "Slackey",
+});
+
+const StyledEmptyListButton = styled(Button)(({ theme }) => ({
+ display: "flex",
+ alignItems: "center",
+ gap: 5,
+ marginTop: 20,
+ width: 200,
+ height: 40,
+ borderRadius: 16,
+ backgroundColor: "transparent",
+ color: theme.palette.mode === "dark" ? "#FBF4EF" : "#453936",
+ border: `1px solid ${theme.palette.mode === "dark" ? "#353531" : "#D5BAA5"}`,
+ fontFamily: "Slackey",
+}));
diff --git a/packages/lib/src/orders/assets/components/ArrowOutward.tsx b/packages/lib/src/orders/assets/components/ArrowOutward.tsx
new file mode 100644
index 000000000..ba93bb2d4
--- /dev/null
+++ b/packages/lib/src/orders/assets/components/ArrowOutward.tsx
@@ -0,0 +1,12 @@
+import type { SVGProps } from "react";
+const SvgArrowOutward = (props: SVGProps) => (
+
+);
+export default SvgArrowOutward;
diff --git a/packages/lib/src/orders/assets/components/Bear.tsx b/packages/lib/src/orders/assets/components/Bear.tsx
new file mode 100644
index 000000000..f9ce484e4
--- /dev/null
+++ b/packages/lib/src/orders/assets/components/Bear.tsx
@@ -0,0 +1,179 @@
+import type { SVGProps } from "react";
+const SvgBear = (props: SVGProps) => (
+
+);
+export default SvgBear;
diff --git a/packages/lib/src/orders/assets/components/index.ts b/packages/lib/src/orders/assets/components/index.ts
new file mode 100644
index 000000000..7545cc7ff
--- /dev/null
+++ b/packages/lib/src/orders/assets/components/index.ts
@@ -0,0 +1,2 @@
+export { default as Bear } from "./Bear";
+export { default as ArrowOutward } from "./ArrowOutward";
diff --git a/packages/lib/src/store.ts b/packages/lib/src/store.ts
index 0708e7efc..e27591476 100644
--- a/packages/lib/src/store.ts
+++ b/packages/lib/src/store.ts
@@ -54,6 +54,8 @@ const getInitialState = (queryParamsEnabled?: boolean): State => {
waitingForOrdersUpdate: false,
srcUsd: undefined,
dstUsd: undefined,
+
+ priceImpact: 0,
};
};
const initialState = getInitialState();
@@ -115,6 +117,10 @@ export const useTwapStore = create(
setQueryParam(QUERY_PARAMS.TRADE_INTERVAL, !fillDelay.amount ? undefined : fillDelay.amount?.toString());
set({ customFillDelay: fillDelay });
},
+ setPriceImpact: (priceImpact: number) => {},
+ getPriceImpact: () => {
+ return get().priceImpact;
+ },
getFillDelayText: (translations: Translations) => fillDelayText((get() as any).getFillDelayUiMillis(), translations),
getFillDelayUiMillis: () => get().customFillDelay.amount! * get().customFillDelay.resolution,
getMinimumDelayMinutes: () => (get().lib?.estimatedDelayBetweenChunksMillis() || 0) / 1000 / 60,
diff --git a/packages/lib/src/styles.ts b/packages/lib/src/styles.ts
index a2413791f..0494fa930 100644
--- a/packages/lib/src/styles.ts
+++ b/packages/lib/src/styles.ts
@@ -21,6 +21,12 @@ export const StyledText = styled(Typography)({
fontSize: "inherit",
});
+export const StyledPriceImpactText = styled(Typography)({
+ fontFamily: "inherit",
+ fontSize: 16,
+ fontWeight: 600,
+});
+
export const StyledColumnFlex = styled(Box)(({ gap = 10 }: { gap?: number }) => ({
display: "flex",
flexDirection: "column",
@@ -158,3 +164,9 @@ export const StyledSummaryRow = styled(StyledRowFlex)({
},
},
});
+
+export const StyledH1 = styled(Typography)(({ theme }) => ({
+ fontSize: 16,
+ fontWeight: 400,
+ color: theme.palette.mode === "dark" ? "#767676" : "#866C65",
+}));
diff --git a/packages/lib/src/types.ts b/packages/lib/src/types.ts
index 36217c018..53baef2b5 100644
--- a/packages/lib/src/types.ts
+++ b/packages/lib/src/types.ts
@@ -80,6 +80,8 @@ export interface Translations {
confirmationLimitPriceTooltip: string;
ordersTooltip: string;
noOrdersFound1: string;
+ noOrdersFound2: string;
+ noOrdersFound_Swap: string;
poweredBy: string;
insertLimitPriceWarning: string;
unwrap: string;
@@ -98,6 +100,8 @@ export interface Translations {
viewOrders: string;
view: string;
estimate: string;
+ partDuration: string;
+ totalDuration: string;
}
export interface BaseComponentProps {
@@ -308,6 +312,8 @@ export interface State {
dstUsd?: BN;
srcUsdLoading?: boolean;
dstUsdLoading?: boolean;
+
+ priceImpact: number;
}
export type SwitchVariant = "ios" | "default";
diff --git a/packages/tradingpost/LICENSE b/packages/tradingpost/LICENSE
new file mode 100644
index 000000000..63f9e3f03
--- /dev/null
+++ b/packages/tradingpost/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Orbs Network
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/tradingpost/package.json b/packages/tradingpost/package.json
new file mode 100644
index 000000000..e070b5269
--- /dev/null
+++ b/packages/tradingpost/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "@orbs-network/twap-ui-tradingpost",
+ "version": "1.0.0",
+ "description": "TWAP UI for Trading Post",
+ "author": "TradingPost",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/orbs-network/twap-ui.git"
+ },
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "./dist"
+ ],
+ "scripts": {
+ "prettier": "prettier -w '{src,test}/**/*.{ts,tsx,js,jsx,json,sol}'",
+ "transform-svg": "ts-node transform-svg.ts",
+ "build": "npm run prettier && rm -rf dist && tsc && npm run copy-assets",
+ "start": "npm run prettier && nodemon --ext js,jsx,ts,tsx,json --watch ./src --exec tsc",
+ "test": "eslint src",
+ "copy-assets": "copyfiles -u 1 src/assets/*.svg dist/"
+ },
+ "dependencies": {
+ "@defi.org/web3-candies": "^4.20",
+ "@mui/material": "5.x",
+ "@mui/system": "5.x",
+ "@orbs-network/twap": "^1.18.x",
+ "@orbs-network/twap-ui": "^0.10.14",
+ "lodash": "4.x",
+ "web3": "1.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "devDependencies": {
+ "@svgr/cli": "^8.1.0",
+ "@types/node": "^22.0.2",
+ "copyfiles": "^2.4.1"
+ }
+}
diff --git a/packages/tradingpost/src/OrderSummary.tsx b/packages/tradingpost/src/OrderSummary.tsx
new file mode 100644
index 000000000..4ac5a6e29
--- /dev/null
+++ b/packages/tradingpost/src/OrderSummary.tsx
@@ -0,0 +1,157 @@
+import { styled } from "@mui/material";
+import { Styles as TwapStyles, Components, store, hooks } from "@orbs-network/twap-ui";
+import { StyledMarketPriceContainer, StyledOrderSummary } from "./styles";
+import { MdArrowDownward } from "@react-icons/all-files/md/MdArrowDownward";
+import { useAdapterContext } from "./context";
+import { useCallback, useState } from "react";
+
+export const OrderSummary = ({ onSubmit, disabled, isLimitPanel }: { onSubmit: () => void; disabled?: boolean; isLimitPanel?: boolean }) => {
+ const Button = useAdapterContext().Button;
+ return (
+
+
+
+
+
+
+
+
+
+ {isLimitPanel ? (
+ <>
+
+
+
+
+
+ >
+ ) : (
+ <>
+
+
+
+
+
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const SummaryPrice = () => {
+ const { TradePrice: DappTradePrice } = useAdapterContext();
+ const [inverted, setInvert] = useState(false);
+ const { isLimitOrder, srcToken, dstToken } = store.useTwapStore((store) => ({
+ isLimitOrder: store.isLimitOrder,
+ srcToken: store.srcToken,
+ dstToken: store.dstToken,
+ }));
+ const { isLoading, getToggled } = hooks.useLimitPriceV2();
+ const { marketPrice } = hooks.useMarketPriceV2(inverted);
+ const price = isLimitOrder ? getToggled(inverted, true) : marketPrice?.original;
+ const value = hooks.useFormatNumber({ value: price || "", decimalScale: 5 });
+
+ const onInvert = useCallback(() => {
+ setInvert((prev) => !prev);
+ }, [setInvert]);
+
+ const leftSymbol = inverted ? dstToken?.symbol : srcToken?.symbol;
+ const rightSymbol = inverted ? srcToken?.symbol : dstToken?.symbol;
+
+ return (
+
+ Price
+
+
+ );
+};
+
+const StyledButtonContainer = styled(TwapStyles.StyledRowFlex)({
+ width: "100%",
+ button: {
+ width: "100%",
+ },
+});
+
+const StyledSummaryDetails = styled(TwapStyles.StyledColumnFlex)({
+ gap: 9,
+ ".twap-token-logo": {
+ display: "none",
+ },
+ "@media(max-width: 700px)": {
+ gap: 6,
+ },
+});
+
+const TokenDisplay = ({ isSrc }: { isSrc?: boolean }) => {
+ const { token, srcAmount } = store.useTwapStore((store) => ({
+ token: isSrc ? store.srcToken : store.dstToken,
+ srcAmount: store.srcAmountUi,
+ }));
+ const dstAmount = hooks.useDstAmount().outAmount.ui;
+
+ const _amount = isSrc ? srcAmount : dstAmount;
+
+ const amount = hooks.useFormatNumber({ value: _amount, decimalScale: 3 });
+
+ return (
+
+ {amount}
+
+ {token?.symbol}
+
+
+
+ );
+};
+
+const StyledTokens = styled(TwapStyles.StyledColumnFlex)({
+ gap: 12,
+ alignItems: "center",
+});
+
+const StyledArrow = styled(MdArrowDownward)({
+ width: 24,
+ height: 24,
+});
+
+const StyledTokenDisplayRight = styled(TwapStyles.StyledRowFlex)({
+ width: "auto",
+ p: {
+ fontSize: 14,
+ },
+ ".twap-token-logo": {
+ width: 24,
+ height: 24,
+ },
+});
+
+const StyledTokenDisplayAmount = styled(TwapStyles.StyledOneLineText)({
+ fontWeight: 600,
+ fontSize: 24,
+});
+const StyledTokenDisplay = styled(TwapStyles.StyledRowFlex)({
+ justifyContent: "space-between",
+ gap: 30,
+});
diff --git a/packages/tradingpost/src/TradingPostOrders.tsx b/packages/tradingpost/src/TradingPostOrders.tsx
new file mode 100644
index 000000000..c591781b3
--- /dev/null
+++ b/packages/tradingpost/src/TradingPostOrders.tsx
@@ -0,0 +1,53 @@
+import * as React from "react";
+import { useMediaQuery } from "@mui/material";
+import { hooks, OrdersPortal, SelectedOrders, store } from "@orbs-network/twap-ui";
+import { StyledOrders, StyledOrdersHeader, StyledOrdersTab, StyledOrdersTabs, StyledBody } from "./styles";
+
+const useMobile = () => {
+ return useMediaQuery("(max-width:700px)");
+};
+
+export default function TradingPostOrders() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+const OrdersHeaderLayout = () => {
+ const mobile = useMobile();
+
+ const { tab, setTab } = store.useOrdersStore();
+
+ const tabs = hooks.useOrdersTabs();
+ const selectedTab = React.useMemo(() => {
+ return Object.keys(tabs)[tab as any];
+ }, [tabs, tab]);
+
+ const onSelect = (index: number) => {
+ setTab(index);
+ };
+
+ return (
+ <>
+
+
+ {Object.keys(tabs)
+ .map((key, index) => {
+ return (
+ onSelect(index)}>
+ {key}
+
+ );
+ })}
+
+
+ >
+ );
+};
diff --git a/packages/tradingpost/src/assets/components/Arrow.tsx b/packages/tradingpost/src/assets/components/Arrow.tsx
new file mode 100644
index 000000000..18045247f
--- /dev/null
+++ b/packages/tradingpost/src/assets/components/Arrow.tsx
@@ -0,0 +1,11 @@
+import * as React from "react";
+import type { SVGProps } from "react";
+const SvgArrow = (props: SVGProps) => (
+
+);
+export default SvgArrow;
diff --git a/packages/tradingpost/src/assets/components/ArrowOutward.tsx b/packages/tradingpost/src/assets/components/ArrowOutward.tsx
new file mode 100644
index 000000000..918f6adef
--- /dev/null
+++ b/packages/tradingpost/src/assets/components/ArrowOutward.tsx
@@ -0,0 +1,13 @@
+import * as React from "react";
+import type { SVGProps } from "react";
+const SvgArrowOutward = (props: SVGProps) => (
+
+);
+export default SvgArrowOutward;
diff --git a/packages/tradingpost/src/assets/components/index.ts b/packages/tradingpost/src/assets/components/index.ts
new file mode 100644
index 000000000..a6c6f8fb9
--- /dev/null
+++ b/packages/tradingpost/src/assets/components/index.ts
@@ -0,0 +1,2 @@
+export { default as ArrowOutward } from "./ArrowOutward";
+export { default as Arrow } from "./Arrow";
diff --git a/packages/tradingpost/src/components.tsx b/packages/tradingpost/src/components.tsx
new file mode 100644
index 000000000..bb8acd0b7
--- /dev/null
+++ b/packages/tradingpost/src/components.tsx
@@ -0,0 +1,52 @@
+import { Components, hooks, store } from "@orbs-network/twap-ui";
+import { useAdapterContext } from "./context";
+import BN from "bignumber.js";
+import { StyledMarketPriceContainer } from "./styles";
+import { styled } from "@mui/material";
+import { useMemo } from "react";
+
+export function Price() {
+ const { TradePrice: DappTradePrice } = useAdapterContext();
+ const { srcToken, dstToken, srcAmount, isLimitOrder } = store.useTwapStore((s) => ({
+ srcToken: s.srcToken,
+ dstToken: s.dstToken,
+ srcAmount: s.getSrcAmount().toString(),
+ isLimitOrder: s.isLimitOrder,
+ }));
+
+ const { limitPrice, isLoading, inverted } = hooks.useLimitPriceV2();
+ const { marketPrice } = hooks.useMarketPriceV2(inverted);
+
+ const price = hooks.useFormatNumber({ value: isLimitOrder ? limitPrice?.toggled : marketPrice?.toggled, decimalScale: 3, disableDynamicDecimals: false });
+
+ if (!DappTradePrice) {
+ return ;
+ }
+
+ if (!srcToken || !dstToken || BN(srcAmount || "0").isZero()) {
+ return null;
+ }
+
+ const leftSymbol = inverted ? dstToken?.symbol : srcToken?.symbol;
+ const rightSymbol = inverted ? srcToken?.symbol : dstToken?.symbol;
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+const StyledLoader = styled(Components.Base.Loader)<{ loading: number }>(({ loading }) => ({
+ position: "absolute",
+ width: "30%!important",
+ height: "15px!important",
+ top: "50%",
+ transform: "translateY(-50%)",
+ right: 0,
+ display: loading ? "block" : ("none" as const),
+ posinterEvents: "none",
+}));
diff --git a/packages/tradingpost/src/context.ts b/packages/tradingpost/src/context.ts
new file mode 100644
index 000000000..11dc9f4ae
--- /dev/null
+++ b/packages/tradingpost/src/context.ts
@@ -0,0 +1,27 @@
+import { TWAPProps } from "@orbs-network/twap-ui";
+import { createContext, FC, JSXElementConstructor, useContext } from "react";
+
+export interface AdapterProps extends TWAPProps {
+ dappTokens?: { [key: string]: any };
+ isDarkTheme?: boolean;
+ ConnectButton: JSXElementConstructor;
+ useTokenModal: any;
+ nativeToken: any;
+ connector?: any;
+ isMobile?: boolean;
+ useTooltip: any;
+ Button: any;
+ ApproveModalContent?: any;
+ SwapTransactionErrorContent?: any;
+ SwapPendingModalContent?: any;
+ SwapTransactionReceiptModalContent?: any;
+ AddToWallet?: any;
+ TradePrice?: any;
+ TradePriceToggle: FC<{ onClick: () => void; loading: boolean }>;
+}
+
+const AdapterContext = createContext({} as AdapterProps);
+
+export const AdapterContextProvider = AdapterContext.Provider;
+
+export const useAdapterContext = () => useContext(AdapterContext);
diff --git a/packages/tradingpost/src/i18n/en.json b/packages/tradingpost/src/i18n/en.json
new file mode 100644
index 000000000..1201d864b
--- /dev/null
+++ b/packages/tradingpost/src/i18n/en.json
@@ -0,0 +1,4 @@
+{
+ "enterAmount": "Enter an amount",
+ "tradeSizeMustBeEqual": "Trade size must be equal to at least 50 USD"
+}
diff --git a/packages/tradingpost/src/index.tsx b/packages/tradingpost/src/index.tsx
new file mode 100644
index 000000000..5628af33d
--- /dev/null
+++ b/packages/tradingpost/src/index.tsx
@@ -0,0 +1,612 @@
+import { GlobalStyles, Box, ThemeProvider, Typography, styled } from "@mui/material";
+import {
+ Components,
+ hooks,
+ Translations,
+ TwapAdapter,
+ Styles as TwapStyles,
+ store,
+ Orders,
+ TwapContextUIPreferences,
+ Styles,
+ TooltipProps,
+ parseError,
+} from "@orbs-network/twap-ui";
+import translations from "./i18n/en.json";
+import {
+ Card,
+ configureStyles,
+ darkTheme,
+ lightTheme,
+ StyledBalanceContainer,
+ StyledColumnFlex,
+ StyledLimitPrice,
+ StyledLimitPriceBody,
+ StyledModalHeader,
+ StyledModalHeaderClose,
+ StyledModalHeaderTitle,
+ StyledPoweredBy,
+ StyledReset,
+ StyledResetLimitButton,
+ StyledSwapModalContent,
+ StyledText,
+ StyledTimeSelect,
+ StyledTimeSelectBody,
+ StyledTimeSelectContainer,
+ StyledTimeSelectHeader,
+} from "./styles";
+import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { StyledBalance, StyledTokenChange, StyledTokenChangeContainer, StyledTokenPanel, StyledTokenPanelInput, StyledTokenSelect } from "./styles";
+import { isNativeAddress, zeroAddress } from "@defi.org/web3-candies";
+import { Configs, TokenData } from "@orbs-network/twap";
+import Web3 from "web3";
+import _ from "lodash";
+import BN from "bignumber.js";
+import { MdArrowDropDown } from "@react-icons/all-files/md/MdArrowDropDown";
+import Arrow from "./assets/components/Arrow";
+import TradingPostOrders from "./TradingPostOrders";
+import { getTokenFromTokensList } from "@orbs-network/twap-ui";
+import { IoMdClose } from "@react-icons/all-files/io/IoMdClose";
+import { OrderSummary } from "./OrderSummary";
+import { useTwapContext } from "@orbs-network/twap-ui";
+import { useAdapterContext, AdapterContextProvider, AdapterProps } from "./context";
+import { Price } from "./components";
+import { create } from "zustand";
+const Button = (props: any) => {
+ const DappButton = useAdapterContext().Button;
+ return (
+
+ {props.children}
+
+ );
+};
+const Tooltip = ({ text, children, childrenStyles = {} }: TooltipProps) => {
+ const useTooltip = useAdapterContext().useTooltip;
+ const { targetRef, tooltip, tooltipVisible } = useTooltip(text, { placement: "top", hideTimeout: 0 });
+ return (
+
+ {children} {tooltipVisible && tooltip}
+
+ );
+};
+const uiPreferences: TwapContextUIPreferences = {
+ usdSuffix: " USD",
+ usdPrefix: "~",
+ usdEmptyUI: <>>,
+ balanceEmptyUI: <>>,
+ switchVariant: "ios",
+ inputPlaceholder: "0.0",
+ Tooltip,
+ Button,
+ orders: {
+ paginationChunks: 4,
+ hideUsd: true,
+ },
+ modal: {
+ styles: {
+ zIndex: 1,
+ },
+ },
+};
+const config = Configs.PancakeSwap;
+export const parseToken = (rawToken: any): TokenData | undefined => {
+ const { address, decimals, symbol, logoURI } = rawToken;
+ if (!symbol) {
+ console.error("Invalid token", rawToken);
+ return;
+ }
+ if (!address || isNativeAddress(address) || address === "BNB") {
+ return config.nativeToken;
+ }
+ return {
+ address: Web3.utils.toChecksumAddress(address),
+ decimals,
+ symbol,
+ logoUrl: logoURI,
+ };
+};
+const storeOverride = {
+ isLimitOrder: true,
+ chunks: 1,
+ customDuration: { resolution: store.TimeResolution.Days, amount: 7 },
+ customFillDelay: { resolution: store.TimeResolution.Minutes, amount: 2 },
+};
+const Balance = ({ isSrc }: { isSrc?: boolean }) => {
+ const onPercentClick = hooks.useCustomActions();
+ return (
+ onPercentClick(1) : () => {}}>
+
+
+ );
+};
+
+const TokenPanel = ({ isSrcToken = false }: { isSrcToken?: boolean }) => {
+ const selectToken = hooks.useSelectTokenCallback();
+ const { dstToken, srcToken } = hooks.useDappRawSelectedTokens();
+ const onSelect = useCallback(
+ (token: any) => {
+ selectToken({ isSrc: !!isSrcToken, token });
+ },
+ [selectToken, isSrcToken]
+ );
+ const onTokenSelectClick = useAdapterContext().useTokenModal(onSelect, srcToken, dstToken, isSrcToken);
+ return (
+
+
+
+
+ {isSrcToken ? "Sell" : "Receive at Least"}
+
+
+
+
+
+
+
+ {" "}
+
+ );
+};
+
+const ChangeTokensOrder = () => {
+ return (
+
+ } />
+
+ );
+};
+const handleAddress = (address?: string) => {
+ return isNativeAddress(address || "") ? "BNB" : address;
+};
+export const useProvider = (props: AdapterProps) => {
+ const [provider, setProvider] = useState(undefined);
+ const setProviderFromConnector = useCallback(async () => {
+ const res = await props.connector?.getProvider();
+ setProvider(res);
+ }, [setProvider, props.connector]);
+ useEffect(() => {
+ setProviderFromConnector();
+ }, [props.account, props.connectedChainId, setProviderFromConnector]);
+ return provider;
+};
+const useTrade = (props: AdapterProps) => {
+ const { srcToken, toToken, srcAmount } = store.useTwapStore((s) => ({
+ srcToken: s.srcToken?.address,
+ toToken: s.dstToken?.address,
+ srcAmount: s.getSrcAmount().toString(),
+ }));
+ const res = props.useTrade!(handleAddress(srcToken), handleAddress(toToken), srcAmount === "0" ? undefined : srcAmount);
+ return {
+ outAmount: res?.outAmount,
+ isLoading: BN(srcAmount || "0").gt(0) && res?.isLoading,
+ };
+};
+const TWAP = memo((props: AdapterProps) => {
+ const provider = useProvider(props);
+ const trade = useTrade(props);
+ const theme = useMemo(() => {
+ return props.isDarkTheme ? darkTheme : lightTheme;
+ }, [props.isDarkTheme]);
+ const dappTokens = useMemo(() => {
+ if (!props.dappTokens || !props.nativeToken) return undefined;
+ return {
+ ...props.dappTokens,
+ [zeroAddress]: props.nativeToken,
+ };
+ }, [props.dappTokens, props.nativeToken]);
+ return (
+
+
+
+
+
+ {props.limit ? : }
+
+
+
+
+
+ );
+});
+const TopPanel = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+const LimitTopPanel = () => {
+ return (
+
+
+
+
+
+
+ );
+};
+const OpenConfirmationModalButton = () => {
+ const { ConnectButton, provider, Button } = useAdapterContext();
+ const { onClick, text, disabled } = useShowSwapModalButton();
+ if (!provider) {
+ return (
+
+
+
+ );
+ }
+ return (
+
+
+
+ );
+};
+const StyledButtonContainer = styled("div")({
+ width: "100%",
+ "> *": {
+ width: "100%",
+ },
+ marginTop: 10,
+});
+
+const PartDuration = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const TotalDuration = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const LimitPanel = () => {
+ const { onInvert } = hooks.useLimitPriceV2();
+ return (
+
+ );
+};
+const TWAPPanel = () => {
+ return (
+
+ );
+};
+const LimitPrice = ({ limitOnly }: { limitOnly?: boolean }) => {
+ const isLimitOrder = store.useTwapStore((store) => store.isLimitOrder);
+ const { onInvert, isLoading } = hooks.useLimitPriceV2();
+ const { TradePriceToggle } = useAdapterContext();
+ return (
+
+
+ {isLimitOrder && (
+
+
+
+
+
+ Set To Market
+
+
+
+
+
+
+
+
+ )}
+
+
+ );
+};
+export { TWAP, Orders };
+export enum SwapState {
+ REVIEW,
+ APPROVE,
+ ATTEMTPING_TX,
+ PENDING_CONFIRMATION,
+ ERROR,
+ COMPLETED,
+}
+interface Store {
+ swapState: SwapState;
+ setSwapState: (value: SwapState) => void;
+}
+export const useOrdersStore = create((set, get) => ({
+ swapState: SwapState.REVIEW,
+ setSwapState: (swapState) => set({ swapState }),
+}));
+const SwapModal = ({ limitPanel }: { limitPanel: boolean }) => {
+ const { swapState, setSwapState } = useOrdersStore();
+ const { dappTokens, ApproveModalContent, SwapPendingModalContent, SwapTransactionErrorContent, AddToWallet, SwapTransactionReceiptModalContent } = useAdapterContext();
+ const { fromToken, setShowConfirmation, showConfirmation, txHash, isLimitOrder, disclaimerAccepted } = store.useTwapStore((s) => ({
+ fromToken: s.srcToken,
+ setShowConfirmation: s.setShowConfirmation,
+ showConfirmation: s.showConfirmation,
+ txHash: s.txHash,
+ isLimitOrder: s.isLimitOrder,
+ disclaimerAccepted: s.disclaimerAccepted,
+ }));
+ const reset = hooks.useResetStore();
+ const { mutateAsync: approveCallback } = hooks.useApproveToken(true);
+ const { data: allowance, isLoading, refetch: refetchAllowance } = hooks.useHasAllowanceQuery();
+ const { mutateAsync: createOrder } = hooks.useCreateOrder(true);
+ const inputCurrency = useMemo(() => getTokenFromTokensList(dappTokens, fromToken?.address), [dappTokens, fromToken]);
+ const [error, setError] = useState("");
+ const { data: hasNativeBalance } = hooks.useHasMinNativeTokenBalance("0.0035");
+ const id = useRef(1);
+ const onSubmit = useCallback(async () => {
+ let _id = id.current;
+ try {
+ if (!hasNativeBalance) {
+ setError(`Insufficient BNB balance, you need at least 0.0035BNB to cover the transaction fees.`);
+ setSwapState(SwapState.ERROR);
+ return;
+ }
+ if (!allowance) {
+ setSwapState(SwapState.APPROVE);
+ await approveCallback();
+ const approved = await refetchAllowance();
+ if (!approved.data) {
+ setError("Insufficient allowance to perform the swap. Please approve the token first.");
+ setSwapState(SwapState.ERROR);
+ return;
+ }
+ }
+ if (id.current === _id) {
+ setSwapState(SwapState.ATTEMTPING_TX);
+ }
+ await createOrder();
+ if (id.current === _id) {
+ setSwapState(SwapState.COMPLETED);
+ }
+ } catch (error) {
+ if (id.current === _id) {
+ setSwapState(SwapState.ERROR);
+ setError(parseError(error) || "An error occurred");
+ }
+ }
+ }, [allowance, approveCallback, createOrder, setSwapState, setError, hasNativeBalance, id]);
+ const wrongNetwork = store.useTwapStore((store) => store.wrongNetwork);
+ let content = null;
+ let title: string | undefined = undefined;
+ const resetPoupupState = () => {
+ setTimeout(() => {
+ setSwapState(SwapState.REVIEW);
+ }, 300);
+ };
+ const onClose = () => {
+ id.current = id.current + 1;
+ setShowConfirmation(false);
+ resetPoupupState();
+ if (txHash) {
+ reset({ waitingForOrdersUpdate: true });
+ }
+ if (swapState === SwapState.COMPLETED) {
+ reset();
+ }
+ };
+ useEffect(() => {
+ if (txHash && swapState === SwapState.ATTEMTPING_TX) {
+ setSwapState(SwapState.PENDING_CONFIRMATION);
+ }
+ }, [txHash, swapState]);
+ const addToWallet = !AddToWallet ? null : ;
+ if (swapState === SwapState.REVIEW) {
+ title = "Confirm Order";
+ content = ;
+ }
+ if (swapState === SwapState.APPROVE) {
+ content = !ApproveModalContent ? null : ;
+ }
+ if (swapState === SwapState.ERROR) {
+ content = !SwapTransactionErrorContent ? null : {}} onDismiss={onClose} message={error} />;
+ }
+ if (swapState === SwapState.ATTEMTPING_TX) {
+ content = !SwapPendingModalContent ? null : (
+ {addToWallet}
+ );
+ }
+ if (swapState === SwapState.PENDING_CONFIRMATION) {
+ content = (
+
+ {addToWallet}
+
+ );
+ }
+ if (swapState === SwapState.COMPLETED) {
+ content = (
+
+ {addToWallet}
+
+ );
+ }
+ if (wrongNetwork) {
+ content = null;
+ }
+ return (
+ } title={title} onClose={onClose} open={showConfirmation}>
+
+ {content}
+
+
+ );
+};
+const StyledSwapModalContentChildren = styled("div")`
+ flex: 1;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+`;
+const ModalHeader = ({ title, onClose }: { title?: string; onClose: () => void }) => {
+ return (
+
+ {title && {title}}
+
+
+
+
+ );
+};
+export const useShowSwapModalButton = () => {
+ const translations = useTwapContext()?.translations;
+ const { shouldWrap, shouldUnwrap, wrongNetwork, setShowConfirmation, createOrderLoading, srcAmount } = store.useTwapStore((store) => ({
+ maker: store.lib?.maker,
+ shouldWrap: store.shouldWrap(),
+ shouldUnwrap: store.shouldUnwrap(),
+ wrongNetwork: store.wrongNetwork,
+ setShowConfirmation: store.setShowConfirmation,
+ createOrderLoading: store.loading,
+ srcAmount: store.srcAmountUi,
+ }));
+ const warning = hooks.useFillWarning();
+ const { isLoading: dstAmountLoading, dexAmounOut } = hooks.useDstAmount();
+ const { mutate: unwrap, isLoading: unwrapLoading } = hooks.useUnwrapToken(true);
+ const { mutate: wrap, isLoading: wrapLoading } = hooks.useWrapToken(true);
+ const { loading: changeNetworkLoading, changeNetwork } = hooks.useChangeNetwork();
+ const srcUsd = hooks.useSrcUsd().value;
+ const dstUsd = hooks.useDstUsd().value;
+ const noLiquidity = useMemo(() => {
+ if (!srcAmount || BN(srcAmount).isZero() || dstAmountLoading) return false;
+ return !dexAmounOut.raw || BN(dexAmounOut.raw).isZero();
+ }, [dexAmounOut.raw, dstAmountLoading, srcAmount]);
+ if (wrongNetwork)
+ return {
+ text: translations.switchNetwork,
+ onClick: changeNetwork,
+ loading: changeNetworkLoading,
+ disabled: changeNetworkLoading,
+ };
+ if (!srcAmount || BN(srcAmount || "0").isZero()) {
+ return {
+ text: translations.enterAmount,
+ disabled: true,
+ };
+ }
+ if (dstAmountLoading) {
+ return { text: "Searching for the best price", onClick: undefined, disabled: true };
+ }
+ if (noLiquidity) {
+ return {
+ text: "Insufficient liquidity for this trade.",
+ disabled: true,
+ loading: false,
+ };
+ }
+ if (!srcUsd || srcUsd.isZero() || !dstUsd || dstUsd.isZero()) {
+ return {
+ text: "Searching for the best price",
+ disabled: true,
+ };
+ }
+ if (warning)
+ return {
+ text: warning,
+ onClick: undefined,
+ disabled: true,
+ loading: false,
+ };
+ if (shouldUnwrap)
+ return {
+ text: translations.unwrap,
+ onClick: unwrap,
+ loading: unwrapLoading,
+ disabled: unwrapLoading,
+ };
+ if (shouldWrap)
+ return {
+ text: translations.wrap,
+ onClick: wrap,
+ loading: wrapLoading,
+ disabled: wrapLoading,
+ };
+ if (createOrderLoading) {
+ return {
+ text: translations.placeOrder,
+ onClick: () => {
+ setShowConfirmation(true);
+ },
+ };
+ }
+ return {
+ text: translations.placeOrder,
+ onClick: () => {
+ setShowConfirmation(true);
+ },
+ loading: false,
+ disabled: false,
+ };
+};
diff --git a/packages/tradingpost/src/styles.tsx b/packages/tradingpost/src/styles.tsx
new file mode 100644
index 000000000..877f8ec3d
--- /dev/null
+++ b/packages/tradingpost/src/styles.tsx
@@ -0,0 +1,979 @@
+import { Box, Button, createTheme, styled, Theme, Typography } from "@mui/material";
+import { display } from "@mui/system";
+import { Components, OrdersContainer, Styles } from "@orbs-network/twap-ui";
+import { CSSProperties, ReactNode } from "react";
+const isDarkMode = (theme: Theme) => theme.palette.mode === "dark";
+
+export const darkTheme = createTheme({
+ palette: {
+ mode: "dark",
+ },
+});
+
+export const lightTheme = createTheme({
+ palette: {
+ mode: "light",
+ },
+});
+
+export const baseStyles = (theme: Theme) => {
+ const darkMode = isDarkMode(theme);
+ return {
+ primaryColor: "#1fc7d4",
+ cardColor: darkMode ? "#362F47" : "#eee",
+ primaryTextColor: darkMode ? "#FBF4EF" : "#453936",
+ secondaryColor: darkMode ? "#767676" : "#866C65",
+ secondarySubTextColor: darkMode ? "#866C65" : "#767676",
+ cardBox: darkMode ? "#2A2A27" : "#E4CAB4",
+ editableCardBox: darkMode ? "#20201D" : "#EFD9C7",
+ inputShadow: darkMode ? "" : "inset 0px 2px 2px -1px rgba(74,74,104,.1)",
+ border: darkMode ? "#383241" : "#e7e3eb",
+ labelIcon: darkMode ? "#f4eeff" : "black",
+ darkMode,
+ subtitle: darkMode ? "#2A2A27" : "#E4CAB4",
+ subValue: darkMode ? "#FBF4EF" : "#453936",
+ };
+};
+
+const getTootlipStyles = (theme: Theme) => {
+ const darkTheme = isDarkMode(theme);
+
+ return {
+ background: darkTheme ? "white" : "#27262C",
+ color: darkTheme ? "#27262C" : "white",
+ fontSize: 15,
+ borderRadius: 10,
+ padding: "10px 15px",
+ lineHeight: "20px",
+ fontWeight: 400,
+ "*": {
+ color: "inherit",
+ },
+ };
+};
+
+const getButtonStyles = (theme: Theme) => {
+ const styles = baseStyles(theme);
+ return {
+ fontWeight: 600,
+ fontSize: 16,
+ boxShadow: "rgba(14, 14, 44, 0.4) 0px -1px 0px 0px inset",
+ borderRadius: 16,
+ background: styles.primaryColor,
+ transition: "0.2s all",
+ color: isDarkMode(theme) ? "#191326" : "white",
+ border: "unset",
+ "&:hover": {
+ opacity: 0.65,
+ },
+ "*, p": {
+ color: isDarkMode(theme) ? "#191326" : "white",
+ },
+ };
+};
+
+export const StyledCardBody = styled(Box)<{ editable?: number; opacity?: number }>(({ theme, editable, opacity = 100 }) => {
+ const styles = baseStyles(theme);
+
+ return {
+ opacity: opacity / 100,
+ display: "flex",
+ flexDirection: "row",
+ width: "100%",
+ pointerEvents: editable ? "all" : "none",
+ background: editable ? styles.editableCardBox : styles.cardBox,
+ padding: 16,
+ borderRadius: 16,
+ boxShadow: "0 2px 0px rgba(213, 186, 165, 0.76)",
+ };
+});
+
+export const StyledPoweredBy = styled(Components.PoweredBy)(({ theme }) => {
+ const styles = baseStyles(theme);
+
+ return {
+ color: styles.primaryTextColor,
+ marginTop: 20,
+ fontSize: 14,
+
+ "*": {
+ color: "inherit",
+ },
+ };
+});
+
+export const configureStyles = (theme: Theme) => {
+ const styles = baseStyles(theme);
+
+ const darkMode = isDarkMode(theme);
+
+ return {
+ ".twap-order-expanded-cancel-wraper": {
+ marginTop: "40px",
+ button: {
+ color: !darkMode ? "white!important" : "#191326!important",
+ },
+ },
+ ".twap-cancel-order": {
+ background: "unset!important",
+ borderRadius: "30px!important",
+ fontWeight: 500,
+ fontSize: "14px!important",
+ padding: "6px 40px!important",
+ transition: "0.2s all",
+ height: "unset!important",
+ cursor: "pointer",
+ marginTop: "20px",
+ minHeight: "unset!important",
+ boxShadow: "unset!important",
+ },
+ ".twap-token-input-loading": {
+ opacity: 0.5,
+ },
+ ".twap-odnp-button": {
+ ...getButtonStyles(theme),
+ background: darkMode ? "#B8ADD2" : "#492F79",
+ padding: "6px 12px!important",
+ width: "fit-content",
+ boxShadow: "unset",
+ marginLeft: "auto",
+ fontWeight: 500,
+ "&-children": {
+ gap: "5px!important",
+ },
+ },
+ ".twap-label": {
+ p: {
+ fontWeight: "400!important",
+ color: styles.primaryTextColor,
+ },
+ svg: {
+ color: `${styles.labelIcon}!important`,
+ maxWidth: 14,
+ maxHeight: 14,
+ },
+ },
+ ".twap-container": {
+ color: styles.primaryTextColor,
+ },
+ ".twap-button": {
+ minHeight: 48,
+ ...getButtonStyles(theme),
+ padding: "10px",
+ },
+ ".twap-order-separator": {
+ background: `${styles.primaryTextColor}!important`,
+ opacity: 0.4,
+ },
+ ".twap-spinner": {
+ color: `${styles.primaryTextColor}!important`,
+ },
+ ".twap-orders-lists": {
+ width: "100%",
+ },
+ ".twap-orders-list": {
+ padding: 0,
+ width: "100%",
+ gap: "15px!important",
+ },
+ ".twap-order-token-display": {
+ flex: "unset!important",
+ },
+ ".twap-adapter-wrapper": {
+ width: "100%",
+ background: "transparent",
+ },
+ ".twap-modal": {
+ ".MuiBackdrop-root": {
+ background: darkMode ? "rgba(244, 238, 255, 0.6)" : "",
+ },
+ },
+
+ ".twap-modal-content": {
+ background: darkMode ? "#27262C" : "white",
+ color: styles.primaryTextColor,
+ display: "flex",
+ flexDirection: "column",
+ padding: "0px",
+ maxWidth: "370px!important",
+ borderRadius: 32,
+ minHeight: "415px",
+ overflow: "hidden",
+ "*::-webkit-scrollbar": {
+ display: "none",
+ },
+ "&-header": {
+ marginBottom: 10,
+ },
+ },
+ ".twap-trade-size": {
+ ".twap-label": {
+ whiteSpace: "nowrap",
+ },
+ ".twap-token-logo": {
+ display: "none",
+ },
+ "*": {
+ color: styles.primaryTextColor,
+ },
+ },
+ ".twap-orders-title": {
+ p: {
+ fontWeight: 600,
+ color: `${styles.primaryTextColor}!important`,
+ },
+ },
+ ".twap-order": {
+ border: `1px solid ${styles.border}`,
+ borderRadius: 16,
+ padding: 15,
+ transition: "0.2s all",
+ color: `${styles.primaryTextColor}!important`,
+ background: darkMode ? "#362F47" : "#EEEAF4",
+ ".twap-order-expanded-right": {
+ fontWeight: "400!important",
+ },
+ ".twap-market-price-section": {
+ "*": {
+ fontSize: 13,
+ fontWeight: "400!important",
+ },
+ },
+ },
+ ".twap-order-progress": {
+ background: darkMode ? "#2D2836!important" : "#1fc7d4!important",
+ "&::after": {
+ display: "none!important",
+ },
+ ".MuiLinearProgress-bar": {
+ background: darkMode ? `${styles.subtitle}!important` : "#7a6eaa!important",
+ },
+ },
+
+ ".twap-switch": {
+ ".MuiSwitch-thumb": {
+ background: darkMode ? `#27262C!important` : "white!important",
+ },
+ ".MuiSwitch-track": {
+ backgroundColor: darkMode ? `#b8add2!important` : "#EDEAF4!important",
+ opacity: "1!important",
+ },
+ ".Mui-checked+.MuiSwitch-track": {
+ background: "#32D0AA!important",
+ },
+ },
+ ".twap-time-selector": {
+ ".twap-input": {
+ input: {
+ fontWeight: 400,
+ },
+ },
+ },
+ ".twap-price-impact-selector": {
+ border: `1px solid ${styles.cardBox}`,
+ borderRadius: 16,
+
+ ".twap-input": {
+ input: {
+ fontWeight: 700,
+ fontSize: 16,
+ },
+ },
+ },
+ ".twap-time-selector-selected": {
+ "*": {
+ color: `${styles.primaryTextColor}!important`,
+ },
+ },
+ ".twap-time-selector-list": {
+ background: styles.cardBox,
+ border: `1px solid ${styles.border}`,
+ borderRadius: "16px!important",
+ padding: "0px!important",
+ },
+ ".twap-time-selector-list-item": {
+ p: { color: styles.primaryTextColor, fontWeight: "400!important" },
+
+ "&:hover": {
+ background: darkMode ? "rgba(255,255,255, 0.06)" : "rgba(0,0,0, 0.06)",
+ },
+ },
+ ".twap-button-disabled": {
+ background: darkMode ? "#3c3742!important" : "#e9eaeb!important",
+ opacity: "1!important",
+ cursor: "not-allowed!important",
+ boxShadow: "unset!important",
+ "*": {
+ color: "#bdc2c4!important",
+ },
+ p: {
+ opacity: "0.4!important",
+ },
+ },
+ ".twap-tooltip": {
+ ".MuiTooltip-arrow": {
+ color: darkMode ? "white!important" : "#27262C!important",
+ },
+ "& .MuiTooltip-tooltip": {
+ ...getTootlipStyles(theme),
+ fontFamily: "Kanit",
+ },
+ },
+ ".twap-loader": {
+ background: darkMode ? "rgba(255,255,255, 0.1)!important" : "rgba(0,0,0, 0.1)!important",
+ right: 0,
+ },
+ ".twap-market-price": {
+ justifyContent: "center!important",
+ width: "100%",
+ ".twap-price-compare": {
+ justifyContent: "center",
+ },
+ ">div": {
+ width: "100%",
+ },
+ "*": {
+ fontSize: 14,
+ color: styles.primaryTextColor,
+ },
+ },
+ ".twap-label, .twap-market-price .title": {
+ fontSize: 13,
+ color: styles.primaryTextColor,
+ fontWeight: 600,
+ "*, p": {
+ color: "inherit",
+ fontWeight: "inherit",
+ },
+ },
+ ".twap-input": {
+ input: {
+ color: styles.primaryTextColor,
+ fontSize: 24,
+ fontWeight: 700,
+ "&::placeholder": {
+ color: `${styles.primaryTextColor}!important`,
+ opacity: 0.5,
+ fontWeight: "inherit",
+ },
+ },
+ },
+ ".twap-usd": {
+ fontSize: 12,
+ "*": {
+ color: styles.subtitle,
+ },
+ },
+ "@media (max-width:970px)": {
+ ".twap-orders-title": {
+ p: {
+ fontSize: "14px!important",
+ },
+ },
+ ".twap-order-expanded": {
+ ".twap-token-logo": {
+ display: "none",
+ },
+ },
+ ".twap-order-preview-tokens": {
+ ".twap-order-preview-icon svg": {
+ width: "16px!important",
+ height: "16px!important",
+ position: "relative",
+ top: 5,
+ },
+ ".twap-token-logo": {
+ display: "none",
+ },
+ },
+ },
+ };
+};
+
+export const StyledText = styled(Styles.StyledText)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ color: styles.secondaryColor,
+ fontSize: 16,
+ fontWeight: 400,
+ };
+});
+
+export const StyledTokenPanelInput = styled(Components.TokenPanelInput)({
+ input: {
+ width: "100%",
+ },
+});
+
+export const StyledBalanceContainer = styled("div")({
+ flex: 1,
+ overflow: "hidden",
+ display: "flex",
+ justifyContent: "flex-end",
+});
+export const StyledBalance = styled(Components.TokenBalance)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ cursor: "pointer",
+ fontSize: 12,
+ color: styles.primaryTextColor,
+ fontWeight: "400",
+ "*": {
+ color: "inherit",
+ fontWeight: "700",
+ },
+ };
+});
+
+export const StyledMarketPrice = styled(Components.MarketPrice)({
+ flexDirection: "column",
+ alignItems: "flex-start",
+
+ gap: 5,
+ ".twap-loader": {
+ marginLeft: "auto",
+ },
+
+ ".twap-price-compare": {
+ justifyContent: "flex-end",
+ width: "auto",
+ marginLeft: "auto",
+ "*": {
+ fontSize: 13,
+ },
+ },
+});
+
+export const StyledMarketPriceContainer = styled(Styles.StyledRowFlex)(({ theme }) => {
+ const darkMode = baseStyles(theme).darkMode;
+ return {
+ position: "relative",
+ padding: 12,
+ borderRadius: 16,
+ background: darkMode ? "#20201D" : "#EFD9C7",
+ justifyContent: "space-between",
+ p: { color: darkMode ? "#767676" : "#866C65", fontWeight: "400!important" },
+ ".twap-token-logo": {
+ display: "none",
+ },
+ ".twap-label": {
+ p: {
+ whiteSpace: "nowrap",
+ },
+ },
+ "@media(max-width: 700px)": {
+ ".twap-label": {
+ p: {
+ fontSize: "12px!important",
+ },
+ },
+ },
+ };
+});
+
+export const StyledTokenSelect = styled(Components.TokenSelect)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ background: styles.cardBox,
+ borderRadius: 16,
+ padding: 12,
+
+ ".twap-token-name": {
+ fontWeight: 700,
+ fontSize: 12,
+ color: styles.primaryTextColor,
+ },
+ };
+});
+
+export const StyledColumnFlex = styled(Styles.StyledColumnFlex)({
+ gap: 14,
+});
+
+export const StyledPercentSelect = styled(Styles.StyledRowFlex)({
+ marginTop: 14,
+ gap: 5,
+ justifyContent: "flex-end",
+});
+
+export const StyledTokenChangeContainer = styled(Styles.StyledRowFlex)(({ theme }) => {
+ const styles = baseStyles(theme);
+
+ const darkMode = isDarkMode(theme);
+ return {
+ width: 32,
+ height: 32,
+ marginLeft: "auto",
+ marginRight: "auto",
+ "&:hover": {
+ button: {
+ opacity: darkMode ? 0.65 : 1,
+ },
+ },
+ };
+});
+
+export const StyledTokenChange = styled(Components.ChangeTokensOrder)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ button: {
+ width: "100%",
+ height: "100%",
+ transition: "unset",
+ svg: {
+ color: styles.primaryTextColor,
+ fill: styles.primaryTextColor,
+ width: 17,
+ height: 17,
+ },
+ },
+ };
+});
+
+export const StyledLimitPrice = styled(Styles.StyledRowFlex)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ justifyContent: "space-between",
+ marginTop: 10,
+ ".twap-limit-price-input": {
+ "*": {
+ color: styles.primaryTextColor,
+ },
+ input: {
+ position: "relative",
+ top: -2,
+ },
+ },
+ ".twap-token-logo": {
+ display: "none",
+ },
+ ".twap-limit-reset": {
+ left: 10,
+ "*": {
+ stroke: styles.primaryColor,
+ },
+ },
+ };
+});
+
+export const StyledLimitPriceInput = styled(Components.LimitPriceInput)({
+ paddingLeft: 0,
+});
+
+const borderButtonStyles = {
+ background: "unset",
+ borderRadius: 16,
+ fontWeight: 600,
+ fontSize: 12,
+ border: "2px solid #1fc7d4",
+ color: "#1fc7d4",
+ padding: "0px 8px",
+ transition: "0.2s all",
+ cursor: "pointer",
+ "&:hover": {
+ opacity: 0.65,
+ },
+};
+
+export const StyledButton = styled("button")<{ selected?: number }>(({ theme, selected }) => {
+ const styles = baseStyles(theme);
+ return {
+ ...borderButtonStyles,
+ background: selected ? styles.primaryColor : "unset",
+ color: !selected ? "#1fc7d4" : styles.darkMode ? "#191326" : "white",
+ };
+});
+
+export const StyledReset = styled(StyledButton)({
+ p: {
+ fontSize: 13,
+ color: "#DE7F3B",
+ },
+ border: "none",
+ padding: "0px",
+});
+
+export const StyledAcceptDisclaimer = styled(Components.AcceptDisclaimer)({
+ justifyContent: "space-between",
+});
+
+export const StyledOutputAddress = styled(Components.OutputAddress)({
+ marginTop: 20,
+ fontSize: 14,
+});
+
+export const StyledOrderSummary = styled(Styles.StyledColumnFlex)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ ".twap-order-summary-output-address": {
+ alignItems: "center",
+ fontSize: 14,
+ },
+ ".twap-order-summary-details-item": {
+ ".twap-label": {
+ maxWidth: "unset",
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ ".twap-token-display": {
+ order: 1,
+ },
+ },
+ ".twap-order-summary-limit-price": {
+ p: {
+ fontSize: 14,
+ },
+ },
+ ".twap-order-summary-details-item-right": {
+ fontSize: 14,
+ gap: 3,
+ },
+ ".twap-ui-close": {
+ "*": {
+ color: `${styles.primaryTextColor}`,
+ },
+ },
+ ".twap-card": {
+ border: `1px solid ${styles.border}`,
+ borderRadius: 16,
+ padding: 12,
+ transition: "0.2s all",
+
+ background: styles.darkMode ? "#353547" : "#D5BAA5",
+ "*": {
+ color: `${styles.primaryTextColor}`,
+ },
+ },
+ ".twap-label": {
+ p: {
+ color: styles.primaryTextColor,
+ },
+ svg: {
+ color: `${styles.labelIcon}!important`,
+ },
+ },
+
+ ".twap-orders-summary-token-display-amount": {
+ p: {
+ fontSize: 16,
+ },
+ },
+ ".twap-orders-summary-token-display": {
+ ".twap-token-logo": {
+ width: 35,
+ height: 35,
+ },
+ },
+ "@media (max-width:700px)": {
+ ".twap-order-summary-limit-price": {
+ "*": {
+ fontSize: "12px!important",
+ },
+ },
+ },
+ };
+});
+
+export const Card = ({ children, className = "" }: { children: ReactNode; className?: string }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+const CardHeader = ({ children, className = "" }: { children: ReactNode; className?: string }) => {
+ return (
+
+ {" "}
+ {children}
+
+ );
+};
+
+const CardBody = ({ children, editable, className = "", opacity }: { children: ReactNode; editable?: boolean; className?: string; opacity?: number }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+Card.Body = CardBody;
+Card.Header = CardHeader;
+
+export const StyledDisabledCardBody = styled(Card.Body)(({ opacity = 1 }: { opacity: number }) => ({
+ opacity: opacity,
+}));
+
+export const StyledTokenPanel = styled(Card)({
+ width: "100%",
+ gap: 7,
+ ".twap-input": {
+ width: "100%",
+ input: {
+ textAlign: "left",
+ },
+ },
+ ".twap-token-logo": {
+ width: 24,
+ height: 24,
+ },
+});
+
+export const StyledTradeSize = styled(Styles.StyledRowFlex)({
+ justifyContent: "space-between",
+ flexWrap: "wrap",
+ alignItems: "center",
+});
+
+export const StyledOrders = styled(OrdersContainer)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ gap: 0,
+ ".twap-orders-empty-list": {
+ marginBottom: "40px",
+ paddingTop: "30px",
+ color: styles.primaryTextColor,
+ justifyContent: "center",
+ alignItems: "center",
+ display: "flex",
+ flexDirection: "column",
+ },
+ ".twap-orders-pagination": {
+ color: styles.primaryTextColor,
+ "*": {
+ color: styles.primaryTextColor,
+ },
+ },
+ };
+});
+
+export const StyledTimeSelect = styled(Styles.StyledColumnFlex)({
+ display: "flex",
+ alignItems: "flex-end",
+ width: "auto",
+ padding: 2,
+ flex: 1,
+});
+
+export const StyledTimeSelectBody = styled(CardBody)({
+ display: "flex",
+ alignItems: "center",
+
+ padding: "4px 10px",
+ width: "auto",
+ boxShadow: "none",
+});
+
+export const StyledTimeSelectContainer = styled(Styles.StyledRowFlex)(({ theme }) => {
+ const styles = baseStyles(theme);
+
+ return {
+ background: styles.editableCardBox,
+ borderRadius: 16,
+ padding: 16,
+ ".MuiButtonBase-root": {
+ padding: "0px!important",
+ background: "unset!important",
+ height: "100%",
+ p: {
+ fontSize: "12px!important",
+ fontWeight: 400,
+ },
+ },
+ ".twap-input": {
+ input: {
+ fontSize: 14,
+ paddingRight: 3,
+ },
+ },
+ };
+});
+
+export const StyledTimeSelectHeader = styled(Card.Header)({
+ marginTop: 1,
+ width: "auto",
+});
+
+export const StyledOrdersHeader = styled(Box)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ background: styles.darkMode ? "#20201D" : "#EFD9C7",
+ borderTopLeftRadius: 16,
+ borderTopRightRadius: 16,
+ height: 60,
+ width: "100%",
+ padding: "0px 0px",
+ position: "relative",
+ };
+});
+
+export const StyledOrdersTab = styled(Box)<{ selected: number }>(({ selected, theme }) => {
+ const styles = baseStyles(theme);
+ const selectedColor = styles.darkMode ? "#FBF4EF" : "#453936";
+ return {
+ cursor: "pointer",
+ background: selected ? (theme.palette.mode === "dark" ? "#2A2A27" : "#E4CAB4") : "transparent",
+ height: "100%",
+ padding: "0px 24px",
+ display: "flex",
+ alignItems: "center",
+ borderRadius: "16px",
+ flex: 1,
+ justifyContent: "center",
+ color: selectedColor,
+ "@media (max-width:700px)": {
+ fontSize: 12,
+ padding: "0px 10px",
+ },
+ };
+});
+
+export const StyledOrdersTabs = styled(Box)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ display: "flex",
+ flex: 1,
+ border: `1px solid ${styles.darkMode ? "#353531" : "#D5BAA5"}`,
+ alignItems: "center",
+ padding: "2px",
+ borderRadius: 16,
+ justifyContent: "space-between",
+ height: "100%",
+
+ "@media (max-width:700px)": {},
+ };
+});
+
+export const StyledLimitPriceBody = styled(Card.Body)({
+ padding: "10px 10px",
+ alignItems: "center",
+ position: "relative",
+ input: {
+ textAlign: "right",
+ },
+});
+
+export const StyledResetLimitButton = styled(Components.ResetLimitButton)({
+ position: "absolute",
+ top: "0",
+ right: "0",
+ zIndex: 1,
+});
+
+export const StyledLimitPriceLabel = styled(Styles.StyledRowFlex)({
+ width: "auto",
+ minHeight: 24,
+});
+
+export const StyledSubmitButtonContainer = styled(Styles.StyledRowFlex)({
+ button: {
+ width: "100%",
+ },
+});
+
+export const StyledModalHeaderClose = styled("button")(({ theme }) => {
+ const darkMode = baseStyles(theme).darkMode;
+
+ return {
+ margin: 0,
+ marginLeft: "auto",
+ background: "transparent",
+ padding: 0,
+ border: "unset",
+ width: 48,
+ height: 48,
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ cursor: "pointer",
+ svg: {
+ color: darkMode ? "#f4eeff" : "#1fc7d4",
+ width: 20,
+ height: 20,
+ },
+ "&:hover": {
+ opacity: 0.8,
+ },
+ };
+});
+
+export const StyledModalHeader = styled(Styles.StyledRowFlex)<{ withTitle: number }>(({ theme, withTitle }) => {
+ const darkMode = baseStyles(theme).darkMode;
+
+ return {
+ justifyContent: "space-between",
+ alignItems: "center",
+ background: !withTitle ? "transparent" : darkMode ? "#3B394D" : "linear-gradient(111.68deg,#f2ecf2,#e8f2f6)",
+ padding: "12px 24px",
+ paddingBottom: !withTitle ? "0" : "12px",
+ borderBottom: !withTitle ? "1px solid transparent" : darkMode ? "1px solid #383241" : "1px solid #e7e3eb",
+ };
+});
+
+export const StyledSwapModalContent = styled(Styles.StyledColumnFlex)<{ style: CSSProperties }>(({ style }) => ({
+ padding: "0px 24px 24px 24px",
+ alignItems: "center",
+ justifyContent: "center",
+ flex: 1,
+ overflowY: "auto",
+ ...style,
+}));
+export const StyledModalHeaderTitle = styled(Typography)(({ theme }) => {
+ const darkMode = baseStyles(theme).darkMode;
+ return {
+ fontSize: 20,
+ fontWeight: 600,
+ color: darkMode ? "#f4eeff" : "#280d5f",
+ };
+});
+
+export const StyledBody = styled(Styles.StyledColumnFlex)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ padding: "15px 20px 20px 20px",
+ borderBottomLeftRadius: 16,
+ borderBottomRightRadius: 16,
+ alignItems: "center",
+ gap: 15,
+ backgroundColor: styles.darkMode ? "#20201D" : "#EFD9C7",
+ };
+});
+
+export const StyledButtonTab = styled("button")<{ selected?: boolean }>(({ selected, theme }) => {
+ const styles = baseStyles(theme);
+
+ let color;
+
+ if (styles.darkMode) {
+ color = selected ? "#DE7F3B" : "#767676";
+ } else {
+ color = selected ? "#DE7F3B" : "#866C65";
+ }
+
+ return {
+ color: color,
+ backgroundColor: "inherit",
+ border: "none",
+ padding: "5px",
+ cursor: "pointer",
+ outline: "none",
+ fontSize: 16,
+ fontWeight: 700,
+ };
+});
+
+export const StyledLine = styled(Box)(({ theme }) => {
+ const styles = baseStyles(theme);
+ return {
+ height: "1px",
+ backgroundColor: styles.darkMode ? "#FBF4EF" : "#D5BAA5",
+ width: "95%",
+ margin: "0 auto",
+ };
+});
diff --git a/packages/tradingpost/src/types.ts b/packages/tradingpost/src/types.ts
new file mode 100644
index 000000000..2cd7853f8
--- /dev/null
+++ b/packages/tradingpost/src/types.ts
@@ -0,0 +1,6 @@
+export interface TradeStatCardProps {
+ title: string;
+ value: string;
+ editable?: boolean;
+ opacity?: number;
+}
diff --git a/packages/tradingpost/transform-svg.ts b/packages/tradingpost/transform-svg.ts
new file mode 100644
index 000000000..94c4e038c
--- /dev/null
+++ b/packages/tradingpost/transform-svg.ts
@@ -0,0 +1,12 @@
+import { execSync } from 'child_process';
+import * as path from 'path';
+import * as fs from 'fs';
+
+const svgDir = path.join(__dirname, 'src', 'assets');
+const outputDir = path.join(__dirname, 'src', 'assets', 'components');
+
+if (!fs.existsSync(outputDir)) {
+ fs.mkdirSync(outputDir, { recursive: true });
+}
+
+execSync(`npx @svgr/cli --typescript --out-dir ${outputDir} ${svgDir}`);
diff --git a/packages/tradingpost/tsconfig.json b/packages/tradingpost/tsconfig.json
new file mode 100644
index 000000000..6bb1a2eba
--- /dev/null
+++ b/packages/tradingpost/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "commonjs",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "outDir": "dist",
+ "declaration": true,
+ },
+ "include": ["./src"]
+}