Skip to content

Commit

Permalink
feat(isolated-markets): Enable Isolated Margin Trading (#541)
Browse files Browse the repository at this point in the history
Co-authored-by: tyleroooo <[email protected]>
Co-authored-by: Jeremy Lee <[email protected]>
Co-authored-by: moo-onthelawn <[email protected]>
Co-authored-by: Bill He <[email protected]>
Co-authored-by: aleka <[email protected]>
Co-authored-by: Prashan Dharmasena <[email protected]>
  • Loading branch information
7 people authored Jun 12, 2024
1 parent ce41742 commit 42ce666
Show file tree
Hide file tree
Showing 66 changed files with 2,173 additions and 862 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"@cosmjs/proto-signing": "^0.32.1",
"@cosmjs/stargate": "^0.32.1",
"@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "1.7.69",
"@dydxprotocol/v4-abacus": "^1.7.72",
"@dydxprotocol/v4-client-js": "^1.1.15",
"@dydxprotocol/v4-localization": "^1.1.118",
"@ethersproject/providers": "^5.7.2",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions public/configs/v1/env.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange/en/articles/8981384-withdrawals-on-dydx-chain#h_23e97bc665",
"adjustTargetLeverageLearnMore": "",
"launchIncentive": "https://cloud.chaoslabs.co",
"tradingRewardsLearnMore": "https://docs.dydx.exchange/concepts-trading/rewards_fees_and_parameters",
"exchangeStats": "https://app.mode.com/dydx_eng/reports/58822121650d?secret_key=391d9214fe6aefec35b7d35c",
Expand Down Expand Up @@ -101,6 +102,7 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"adjustTargetLeverageLearnMore": "",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange/en/articles/8981384-withdrawals-on-dydx-chain#h_23e97bc665",
"launchIncentive": "https://cloud.chaoslabs.co",
Expand Down Expand Up @@ -131,6 +133,7 @@
"keplrDashboard": "[HTTP link to keplr dashboard, can be null]",
"strideZoneApp": "[HTTP link to stride zone app, can be null]",
"accountExportLearnMore": "[HTTP link to account export learn more, can be null]",
"adjustTargetLeverageLearnMore": "[HTTP link to adjust target leverage learn more, can be null]",
"walletLearnMore": "[HTTP link to wallet learn more, can be null]",
"withdrawalGateLearnMore": "[HTTP link to withdrawal gate learn more, can be null]",
"launchIncentive": "[HTTP link to launch incentive host, can be null]",
Expand Down
1 change: 1 addition & 0 deletions src/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const $A = styled.a<StyleProps>`
${layoutMixins.spacedRow}
gap: 0.25em;
cursor: pointer;
&:hover {
text-decoration: underline;
Expand Down
48 changes: 43 additions & 5 deletions src/components/PotentialPositionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,63 @@
import { useCallback } from 'react';

import styled from 'styled-components';

import { SubaccountPendingPosition } from '@/constants/abacus';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';

import { useStringGetter } from '@/hooks/useStringGetter';

import { layoutMixins } from '@/styles/layoutMixins';

import { useAppDispatch } from '@/state/appTypes';
import { openDialog } from '@/state/dialogs';

import { AssetIcon } from './AssetIcon';
import { Icon, IconName } from './Icon';
import { Link } from './Link';
import { Output, OutputType } from './Output';

type PotentialPositionCardProps = {
marketName: string;
onViewOrders: (marketId: string) => void;
pendingPosition: SubaccountPendingPosition;
};

export const PotentialPositionCard = ({ onViewOrders }: PotentialPositionCardProps) => {
export const PotentialPositionCard = ({
marketName,
onViewOrders,
pendingPosition,
}: PotentialPositionCardProps) => {
const dispatch = useAppDispatch();
const onCancelOrders = useCallback(
(marketId: string) => {
dispatch(openDialog({ type: DialogTypes.CancelPendingOrders, dialogProps: { marketId } }));
},
[dispatch]
);

const stringGetter = useStringGetter();
const { assetId, freeCollateral, marketId, orderCount } = pendingPosition;

return (
<$PotentialPositionCard>
<$MarketRow>
<AssetIcon symbol="ETH" /> Market Name
<AssetIcon symbol={assetId} />
{marketName}
</$MarketRow>
<$MarginRow>
<$MarginLabel>{stringGetter({ key: STRING_KEYS.MARGIN })}</$MarginLabel>{' '}
<$Output type={OutputType.Fiat} value={1_000} />
<$Output type={OutputType.Fiat} value={freeCollateral?.current} />
</$MarginRow>
<$ActionRow>
<$Link onClick={() => onViewOrders('UNI-USD')}>
{stringGetter({ key: STRING_KEYS.VIEW_ORDERS })} <Icon iconName={IconName.Arrow} />
<$Link onClick={() => onViewOrders(marketId)}>
{stringGetter({ key: orderCount > 1 ? STRING_KEYS.VIEW_ORDERS : STRING_KEYS.VIEW })}{' '}
<Icon iconName={IconName.Arrow} />
</$Link>
<$CancelLink onClick={() => onCancelOrders(marketId)}>
{stringGetter({ key: orderCount > 1 ? STRING_KEYS.CANCEL_ORDERS : STRING_KEYS.CANCEL })}{' '}
</$CancelLink>
</$ActionRow>
</$PotentialPositionCard>
);
Expand All @@ -46,6 +73,7 @@ const $PotentialPositionCard = styled.div`
padding: 0.75rem 0;
border-radius: 0.625rem;
`;

const $MarketRow = styled.div`
${layoutMixins.row}
gap: 0.5rem;
Expand All @@ -56,26 +84,36 @@ const $MarketRow = styled.div`
font-size: 1.25rem; // 20px x 20px
}
`;

const $MarginRow = styled.div`
${layoutMixins.spacedRow}
padding: 0 0.625rem;
margin-top: 0.625rem;
`;

const $MarginLabel = styled.span`
color: var(--color-text-0);
font: var(--font-mini-book);
`;

const $Output = styled(Output)`
font: var(--font-small-book);
`;

const $ActionRow = styled.div`
${layoutMixins.spacedRow}
border-top: var(--border);
margin-top: 0.5rem;
padding: 0 0.625rem;
padding-top: 0.5rem;
`;

const $Link = styled(Link)`
--link-color: var(--color-accent);
font: var(--font-small-book);
`;

const $CancelLink = styled(Link)`
--link-color: var(--color-risk-high);
font: var(--font-small-book);
`;
6 changes: 4 additions & 2 deletions src/components/RadioButtonCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const $Root = styled(Root)`
--radio-button-cards-item-checked-backgroundColor: ;
--radio-button-cards-item-disabled-backgroundColor: ;
--radio-button-cards-item-backgroundColor: ;
--radio-button-cards-item-header-font: ;
--radio-button-cards-item-body-font: ;
`;

const $RadioButtonCard = styled(Item)`
Expand All @@ -63,7 +65,7 @@ const $RadioButtonCard = styled(Item)`
background-color: var(--radio-button-cards-item-backgroundColor, transparent);
border: 1px solid var(--color-layer-6);
padding: var(--radio-button-cards-item-padding, 1rem);
font: var(--font-mini-book);
font: var(--radio-buttons-cards-item-body-font, var(--font-mini-book));
text-align: left;
gap: var(--radio-button-cards-item-gap, 0.5rem);
Expand All @@ -83,7 +85,7 @@ const $CardHeader = styled.div`
align-self: stretch;
align-items: center;
color: var(--color-text-2);
font: var(--font-base-medium);
font: var(--radio-button-cards-item-header-font, var(--font-base-medium));
justify-content: space-between;
gap: 1rem;
`;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export const Table = <TableRowData extends BaseTableRowData | CustomRowConfig>({

const currentBreakpoints = useBreakpoints();
const shownColumns = columns.filter(
({ hideOnBreakpoint }) => !hideOnBreakpoint || !currentBreakpoints[hideOnBreakpoint as string]
({ hideOnBreakpoint }) => !hideOnBreakpoint || !currentBreakpoints[hideOnBreakpoint]
);

const collator = useCollator();
Expand Down
27 changes: 23 additions & 4 deletions src/components/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { DropdownSelectMenu } from '@/components/DropdownSelectMenu';
import { Tag } from '@/components/Tag';
import { Toolbar } from '@/components/Toolbar';

import { getSimpleStyledOutputType } from '@/lib/genericFunctionalComponentUtils';

export type TabItem<TabItemsValue> = {
value: TabItemsValue;
label: React.ReactNode;
Expand All @@ -31,6 +33,7 @@ type ElementProps<TabItemsValue> = {
items: TabItem<TabItemsValue>[];
slotToolbar?: ReactNode;
sharedContent?: ReactNode;
disabled?: boolean;
onValueChange?: (value: TabItemsValue) => void;
onWheel?: (event: React.WheelEvent) => void;
};
Expand All @@ -57,6 +60,7 @@ export const Tabs = <TabItemsValue extends string>({
side = 'top',
withBorders = true,
withTransitions = true,
disabled = false,
className,
}: ElementProps<TabItemsValue> & StyleProps) => {
const currentItem = items.find((item) => item.value === value);
Expand All @@ -67,7 +71,12 @@ export const Tabs = <TabItemsValue extends string>({
{items.map((item) =>
!item.subitems ? (
item.customTrigger ?? (
<$Trigger key={item.value} value={item.value} $withBorders={withBorders}>
<$Trigger
key={item.value}
value={item.value}
$withBorders={withBorders}
disabled={disabled}
>
{item.label}
{item.tag && <Tag>{item.tag}</Tag>}
{item.slotRight}
Expand All @@ -82,6 +91,7 @@ export const Tabs = <TabItemsValue extends string>({
align="end"
$isActive={item.subitems.some((subitem) => subitem.value === value)}
slotTrigger={<$DropdownTabTrigger value={value ?? ''} />}
disabled={disabled}
>
{item.label}
</$DropdownSelectMenu>
Expand Down Expand Up @@ -139,10 +149,13 @@ const tabTriggerStyle = css`
font: var(--trigger-font, var(--font-base-book));
color: var(--trigger-textColor);
background-color: var(--trigger-backgroundColor);
box-shadow: inset 0 calc(var(--trigger-underline-size) * -1) 0 var(--trigger-active-textColor);
&[data-state='active'] {
color: var(--trigger-active-textColor);
background-color: var(--trigger-active-backgroundColor);
box-shadow: inset 0 calc(var(--trigger-active-underline-size) * -1) 0
var(--trigger-active-textColor);
}
`;

Expand All @@ -154,6 +167,9 @@ const $Root = styled(Root)<{ $side: 'top' | 'bottom'; $withInnerBorder?: boolean
--trigger-active-backgroundColor: var(--color-layer-1);
--trigger-active-textColor: var(--color-text-2);
--trigger-active-underline-size: 0px;
--trigger-underline-size: 0px;
/* Variants */
--tabs-currentHeight: var(--tabs-height);
Expand Down Expand Up @@ -300,6 +316,10 @@ const $DropdownTabTrigger = styled(Trigger)`
width: 100%;
`;

const dropdownSelectMenuType = getSimpleStyledOutputType(
DropdownSelectMenu,
{} as { $isActive?: boolean }
);
const $DropdownSelectMenu = styled(DropdownSelectMenu)<{ $isActive?: boolean }>`
--trigger-radius: 0;
--dropdownSelectMenu-item-font-size: var(--fontSize-base);
Expand All @@ -309,10 +329,9 @@ const $DropdownSelectMenu = styled(DropdownSelectMenu)<{ $isActive?: boolean }>`
css`
--trigger-textColor: var(--trigger-active-textColor);
--trigger-backgroundColor: var(--trigger-active-backgroundColor);
--trigger-underline-size: var(--trigger-active-underline-size);
`}
` as <MenuItemValue extends string>(
props: { $isActive?: boolean } & React.ComponentProps<typeof DropdownSelectMenu<MenuItemValue>>
) => ReactNode;
` as typeof dropdownSelectMenuType;

export const MobileTabs = styled(Tabs)`
--trigger-backgroundColor: transparent;
Expand Down
14 changes: 14 additions & 0 deletions src/constants/abacus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export type MarketTrade = Abacus.exchange.dydx.abacus.output.MarketTrade;
export type OrderbookLine = Abacus.exchange.dydx.abacus.output.OrderbookLine;
export type PerpetualMarket = Abacus.exchange.dydx.abacus.output.PerpetualMarket;
export type MarketHistoricalFunding = Abacus.exchange.dydx.abacus.output.MarketHistoricalFunding;
export const PerpetualMarketType = Abacus.exchange.dydx.abacus.output.PerpetualMarketType;

// ------ Configs ------ //
export type Configs = Abacus.exchange.dydx.abacus.output.Configs;
Expand All @@ -102,6 +103,8 @@ export type ClosePositionInputs = Abacus.exchange.dydx.abacus.output.input.Close
export type TradeInputSummary = Abacus.exchange.dydx.abacus.output.input.TradeInputSummary;
export type TransferInputs = Abacus.exchange.dydx.abacus.output.input.TransferInput;
export type TriggerOrdersInputs = Abacus.exchange.dydx.abacus.output.input.TriggerOrdersInput;
export type AdjustIsolatedMarginInputs =
Abacus.exchange.dydx.abacus.output.input.AdjustIsolatedMarginInput;
export type InputError = Abacus.exchange.dydx.abacus.output.input.ValidationError;
export type TransferInputTokenResource =
Abacus.exchange.dydx.abacus.output.input.TransferInputTokenResource;
Expand All @@ -122,6 +125,8 @@ export type HistoricalTradingRewardsPeriods = (typeof historicalTradingRewardsPe

export type Subaccount = Abacus.exchange.dydx.abacus.output.Subaccount;
export type SubaccountPosition = Abacus.exchange.dydx.abacus.output.SubaccountPosition;
export type SubaccountPendingPosition =
Abacus.exchange.dydx.abacus.output.SubaccountPendingPosition;
export type SubaccountOrder = Abacus.exchange.dydx.abacus.output.SubaccountOrder;
export type OrderStatus = Abacus.exchange.dydx.abacus.output.input.OrderStatus;
export const AbacusOrderStatus = Abacus.exchange.dydx.abacus.output.input.OrderStatus;
Expand All @@ -148,6 +153,13 @@ export type TransferInputFields = (typeof transferInputFields)[number];

export const TransferType = Abacus.exchange.dydx.abacus.output.input.TransferType;

export const AdjustIsolatedMarginInputField =
Abacus.exchange.dydx.abacus.state.model.AdjustIsolatedMarginInputField;
const adjustIsolatedMarginInputFields = [...AdjustIsolatedMarginInputField.values()] as const;
export type AdjustIsolatedMarginInputFields = (typeof adjustIsolatedMarginInputFields)[number];
export const IsolatedMarginAdjustmentType =
Abacus.exchange.dydx.abacus.output.input.IsolatedMarginAdjustmentType;

// ------ Trade Items ------ //
export const TradeInputField = Abacus.exchange.dydx.abacus.state.model.TradeInputField;
const tradeInputFields = [...TradeInputField.values()] as const;
Expand Down Expand Up @@ -194,6 +206,8 @@ export type HumanReadableWithdrawPayload =
Abacus.exchange.dydx.abacus.state.manager.HumanReadableWithdrawPayload;
export type HumanReadableTransferPayload =
Abacus.exchange.dydx.abacus.state.manager.HumanReadableTransferPayload;
export type HumanReadableSubaccountTransferPayload =
Abacus.exchange.dydx.abacus.state.manager.HumanReadableSubaccountTransferPayload;

// ------ Helpers ------ //
export const AbacusHelper = Abacus.exchange.dydx.abacus.utils.AbacusHelper;
Expand Down
5 changes: 5 additions & 0 deletions src/constants/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,8 @@ export type Hdkey = {
};

export const AMOUNT_RESERVED_FOR_GAS_USDC = 0.1;

/**
* @description The number of parentSubaccounts: 0 - 127, 128 is the first childSubaccount
*/
export const NUM_PARENT_SUBACCOUNTS = 128;
1 change: 1 addition & 0 deletions src/constants/dialogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum DialogTypes {
AdjustIsolatedMargin = 'AdjustIsolatedMargin',
AdjustTargetLeverage = 'AdjustTargetLeverage',
ClosePosition = 'ClosePosition',
CancelPendingOrders = 'CancelPendingOrders',
ComplianceConfig = 'ComplianceConfig',
Deposit = 'Deposit',
DisconnectWallet = 'DisconnectWallet',
Expand Down
12 changes: 12 additions & 0 deletions src/constants/tooltips/trade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ export const tradeTooltips: TooltipStrings = {
title: stringGetter({ key: TOOLTIP_STRING_KEYS.BUYING_POWER_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.BUYING_POWER_BODY, params: stringParams }),
}),
'cross-margin-usage': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.CROSS_MARGIN_USAGE_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.CROSS_MARGIN_USAGE_BODY }),
}),
'cross-free-collateral': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.CROSS_FREE_COLLATERAL_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.CROSS_FREE_COLLATERAL_BODY }),
}),
'default-execution': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.DEFAULT_EXECUTION_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.DEFAULT_EXECUTION_BODY }),
Expand Down Expand Up @@ -195,6 +203,10 @@ export const tradeTooltips: TooltipStrings = {
title: stringGetter({ key: TOOLTIP_STRING_KEYS.TAKER_FEE_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.TAKER_FEE_BODY }),
}),
'target-leverage': ({ stringGetter, stringParams }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.TARGET_LEVERAGE_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.TARGET_LEVERAGE_BODY, params: stringParams }),
}),
'tick-size': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.TICK_SIZE_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.TICK_SIZE_BODY }),
Expand Down
Loading

0 comments on commit 42ce666

Please sign in to comment.