Compare commits

..
Author SHA1 Message Date
Bill He d17a9005c0 bump v4-client 2023-09-08 16:10:16 -07:00
James Jia - Test 4b86068d8f Initial commit 2023-09-08 13:52:13 -07:00
28 changed files with 232 additions and 252 deletions
+4 -4
View File
@@ -1,4 +1,4 @@
# Contributing to Trader-Fe
# Contributing to V4-web
We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
- Reporting a bug
@@ -18,10 +18,10 @@ Pull requests are the best way to propose changes to the codebase (we use [Githu
4. Issue that pull request!
## Any contributions you make will be under the same License
When you submit code changes, your submissions are understood to be under the same [License](https://github.com/dydxprotocol/trader-fe/blob/master/LICENSE) that covers the project.
When you submit code changes, your submissions are understood to be under the same [License](https://github.com/dydxprotocol/v4-web/blob/master/LICENSE) that covers the project.
## Report bugs using Github's [issues](https://github.com/dydxprotocol/trader-fe/issues)
Report a bug by [opening a new issue](https://github.com/dydxprotocol/trader-fe/issues/new).
## Report bugs using Github's [issues](https://github.com/dydxprotocol/v4-web/issues)
Report a bug by [opening a new issue](https://github.com/dydxprotocol/v4-web/issues/new).
**Great Bug Reports** tend to have:
+9 -6
View File
@@ -1,16 +1,19 @@
Business Source License 1.1
License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.
Copyright (C) 2023 dYdX Trading Inc.
Subject to your compliance with applicable law, you are granted the right to use the Licensed Work (defined below) under the terms of the below licenses; provided, however, that if you violate any such applicable law in your use of the Licensed Work, all of your rights and licenses to use (including any rights to reproduce, distribute, install or modify) the Licensed Work will automatically and immediately terminate.
Use of this software before the Change Date specified in the LICENSE file is governed by the Business Source License included in the LICENSE file and at https://spdx.org/licenses/BUSL-1.1.html.
On and after the Change Date specified in the LICENSE file, use of this software will be governed by the Change License (GNU Affero GPL v3) as specified in the LICENSE file.
Business Source License 1.1
Business Source License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.
Copyright (C) 2023 dYdX Trading Inc.
Parameters
Licensor: dYdX Trading Inc.
Licensed Work: dydxprotocol/cosmos-sdk, dydxprotocol/cometbft, dydxprotocol/v4-chain, dydxprotocol/v4-clients, dydxprotocol/v4-web, dydxprotocol/v4-abacus, dydxprotocol/v4-localization, dydxprotocol/v4-documentation, and any dYdX or dYdX Trading Inc. github repository reflecting a copy of this license, or link to this license.
Additional Use Grant: None
Change Date: On the later to occur of (i) the release of the Licensed Work labeled “dYdX Version 1.0 GPL”, and which includes a document uploaded to the relevant github repository stating “The software in this repository is Version 1.0, subject to GPL license”, and (ii) September 30, 2023 (approximate projected release of “Version 1.0” of this software).
Change Date: On the later to occur of (i) the release of the Licensed Work labeled “dYdX Version 1.0 Affero GPL”, and which includes a document uploaded to the relevant github repository stating “The software in this repository is Version 1.0, subject to Affero GPL license”, and (ii) September 30, 2023 (approximate projected release of “Version 1.0” of this software).
Change License: GNU Affero GPL License v3
License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.
@@ -39,7 +42,7 @@ OPEN SOURCE LICENSE BELOW - TO BE APPLIED AFTER THE CHANGE DATE.
Copyright (C) 2023 dYdX Trading Inc.
Use of this software is governed by the The GNU Affero General Public License as set forth below.
Subject to your compliance with applicable law, you are granted the right to use the Licensed Work (defined below) under the terms of the GNU Affero General Public License as set forth below; provided, however, that if you violate any such applicable law in your use of the Licensed Work, all of your rights and licenses to use (including any rights to reproduce, distribute, install or modify) the Licensed Work will automatically and immediately terminate.
The GNU Affero General Public License
@@ -835,4 +838,4 @@ copy of the Program in return for a fee.
For more information about this software, see https://dydx.exchange.
Copyright (C) 2023 dYdX Trading Inc.
Copyright (C) 2023 dYdX Trading Inc.
+46 -2
View File
@@ -22,8 +22,8 @@ Alternatively, follow the [IPFS Kubo installation guide](https://docs.ipfs.tech
Clone the repository and navigate to its directory:
```bash
git clone https://github.com/dydxprotocol/trader-fe.git
cd trader-fe
git clone https://github.com/dydxprotocol/v4-web.git
cd v4-web
```
### Step 2: Install pnpm and dependencies
@@ -102,3 +102,47 @@ To access your content on IPFS:
2. **Public IPFS gateway:** Access your content via a public IPFS gateway, such as [https://dweb.link](https://dweb.link/) or [https://w3s.link/](https://w3s.link/). Use the gateway URL with your CID appended, like `https://dweb.link/ipfs/your_cid`.
Replace `your_cid` with the actual CID.
## Part 5: Customization
### Using CircularXX font
1. **Procure a license for CircularXX:** Visit [Lineto](https://lineto.com/shop/select?family=circular&set=DOPhtVQR-8m) and add `Circular/Family Package` + `Circular Mono/Regular` to your cart. Check the applicable boxes in the `Configure` step to fill out licensing information. After the `Payment` step, download the `.woff2` files.
2. **Add the fonts to `v4-web`:** Add `CircularXXWeb-Bold.woff2`, `CircularXXWeb-Book.woff2`, `CircularXXWeb-Medium.woff2`, and `CircularXXMonoWeb-Regular.woff2` to the `src/styles/fonts` directory.
3. **Add font-family to stylesheets:** In `src/styles/fonts.css`, add the following code to import your newly purchased `Circular` font.
```css
@font-face {
src: url("fonts/CircularXXWeb-Book.woff2") format("woff2");
font-family: "Circular";
font-weight: 450;
}
@font-face {
src: url("fonts/CircularXXWeb-Medium.woff2") format("woff2");
font-family: "Circular";
font-weight: 500;
}
@font-face {
src: url("fonts/CircularXXWeb-Bold.woff2") format("woff2");
font-family: "Circular";
font-weight: 700;
}
@font-face {
src: url("fonts/CircularXXMonoWeb-Regular.woff2") format("woff2");
font-family: "CircularMono";
font-weight: 400;
}
```
In `src/styles/text.css`, update lines `4` and `5` to
```css
--fontFamily-base: "Circular", "Satoshi", system-ui, -apple-system, Helvetica, Arial, sans-serif;
--fontFamily-monospace: "CircularMono", Courier, monospace, var(--fontFamily-base);
```
Enjoy the newly installed font.
+2 -2
View File
@@ -37,8 +37,8 @@
"@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/abacus": "^0.4.8",
"@dydxprotocol/v4-client-js": "^0.32.0",
"@dydxprotocol/abacus": "^0.4.16",
"@dydxprotocol/v4-client-js": "^0.33.1",
"@dydxprotocol/v4-localization": "^0.0.25",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
+26 -26
View File
@@ -27,11 +27,11 @@ dependencies:
specifier: ^0.31.0
version: 0.31.0
'@dydxprotocol/abacus':
specifier: ^0.4.8
version: 0.4.8
specifier: ^0.4.16
version: 0.4.16
'@dydxprotocol/v4-client-js':
specifier: ^0.32.0
version: 0.32.0
specifier: ^0.33.1
version: 0.33.1
'@dydxprotocol/v4-localization':
specifier: ^0.0.25
version: 0.0.25
@@ -1145,35 +1145,18 @@ packages:
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
dev: true
/@dydxprotocol/abacus@0.4.8:
resolution: {integrity: sha512-hX31m+a5Bwbs4dr5raPDtnflwUzEu/9BIt+LOlQgt1YOViuIse8dozgpgCHvqvA8MttqY3QGPalx5iRB8j8vtw==}
/@dydxprotocol/abacus@0.4.16:
resolution: {integrity: sha512-FTvzgVZ0xjMRmfHwlyoluUSG4Uu4FUHPnaTvLLPl0ER0junmaVDrmBydAUMWMEYGiLyx/k0ln1cLUDrYF2aX6A==}
dev: false
/@dydxprotocol/dydxjs@0.3.0:
resolution: {integrity: sha512-ygNeBs0f3H7sJk1qLUjfNwaXgc5TmjD+qZf7BFDbi7+boHJI9xuOWrGpjfBo7OTUZxS6O5jZG323CgU5XqMSPw==}
dependencies:
'@babel/runtime': 7.22.10
'@bufbuild/buf': 1.19.0-1
'@cosmjs/amino': 0.30.1
'@cosmjs/proto-signing': 0.30.1
'@cosmjs/stargate': 0.30.1
'@cosmjs/tendermint-rpc': 0.30.1
osmojs: 15.5.0
protobufjs: 6.11.4
transitivePeerDependencies:
- bufferutil
- debug
- utf-8-validate
dev: false
/@dydxprotocol/v4-client-js@0.32.0:
resolution: {integrity: sha512-KvKActMgWLcsRQ7RQhpi/F8xeQ7MWUp3G5+4IPNZ4wyRwAxDSbcrfYbTetI/IesHyelRPSeYxam9eYq3wXwZ1A==}
/@dydxprotocol/v4-client-js@0.33.1:
resolution: {integrity: sha512-yFDyiG5aVo4avF5u98yz6BEmWW66SscV2RgmdDwV+LfyKfcubVqKfmv3FtuqE0WKZuzzwiCuFat7YMSK125zyQ==}
dependencies:
'@cosmjs/amino': 0.30.1
'@cosmjs/proto-signing': 0.30.1
'@cosmjs/stargate': 0.30.1
'@cosmjs/tendermint-rpc': 0.30.1
'@dydxprotocol/dydxjs': 0.3.0
'@dydxprotocol/v4-proto': 0.1.2
'@osmonauts/lcd': 0.6.0
'@scure/bip32': 1.3.1
'@scure/bip39': 1.2.1
@@ -1193,6 +1176,23 @@ packages:
resolution: {integrity: sha512-Rp6VHA8z+nwgvTjjB3fvL8gY7bd4DYDHV+NPoL0MFuRm39vhFxGlBVAGDkXPkQTh6YU5BlZV2yXLkDeSfFqEBQ==}
dev: false
/@dydxprotocol/v4-proto@0.1.2:
resolution: {integrity: sha512-kcwdVdAexWB1nq/elFKCO4Su2ONQxBCJbN1TMeIAQiIAXxa7CnmYwdbLt4AIIxjgRhu4cHP8F6sGdwft6cjkCA==}
dependencies:
'@babel/runtime': 7.22.10
'@bufbuild/buf': 1.19.0-1
'@cosmjs/amino': 0.30.1
'@cosmjs/proto-signing': 0.30.1
'@cosmjs/stargate': 0.30.1
'@cosmjs/tendermint-rpc': 0.30.1
osmojs: 15.5.0
protobufjs: 6.11.4
transitivePeerDependencies:
- bufferutil
- debug
- utf-8-validate
dev: false
/@emotion/is-prop-valid@1.2.1:
resolution: {integrity: sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==}
dependencies:
+4
View File
@@ -80,6 +80,8 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
{group.items.map((item) => (
<Fragment key={item.value}>
<Styled.Item
// value={item.value} // search by both value and textContent
// value={[group.groupLabel, item.label, item.tag].filter(Boolean).join('|')} // exclude item.value from searchable terms (not guaranteed to be unique)
value={[group.groupLabel, item.value, item.description, item.label, item.tag]
.filter(Boolean)
.join('|')}
@@ -114,6 +116,8 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
item.subitems?.map((subitem) => (
<Fragment key={subitem.value}>
<Styled.Item
// value={subitem.value} // search by both value and textContent
// value={[group.groupLabel, item.label, subitem.label, subitem.tag].filter(Boolean).join('|')}
value={[
group.groupLabel,
item.value,
+1 -4
View File
@@ -40,10 +40,7 @@ export type AbacusFileSystemProtocol = Omit<
Abacus.exchange.dydx.abacus.protocols.FileSystemProtocol,
'__doNotUseOrImplementIt'
>;
export type AbacusTrackingProtocol = Omit<
Abacus.exchange.dydx.abacus.protocols.TrackingProtocol,
'__doNotUseOrImplementIt'
>;
export type FileLocation = Abacus.exchange.dydx.abacus.protocols.FileLocation;
export type ThreadingType = Abacus.exchange.dydx.abacus.protocols.ThreadingType;
export const CoroutineTimer = Abacus.exchange.dydx.abacus.utils.CoroutineTimer;
-30
View File
@@ -1,36 +1,6 @@
import { Asset, PerpetualMarket } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
export interface Market {
market: string;
status: string;
baseAsset: string;
quoteAsset: string;
stepSize: string;
tickSize: string;
oraclePrice: string;
priceChange24H: string;
nextFundingRate: string;
nextFundingAt: string;
minOrderSize: string;
type: string;
initialMarginFraction: string;
maintenanceMarginFraction: string;
volume24H: string;
trades24H: string;
openInterest: string;
incrementalInitialMarginFraction: string;
incrementalPositionSize: string;
maxPositionSize: string;
baselinePositionSize: string;
assetResolution: string;
syntheticAssetId: string;
}
export type AllMarkets = {
[market: string]: Market;
};
export type MarketData = {
asset: Asset;
tickSizeDecimals: number;
+11 -17
View File
@@ -1,5 +1,4 @@
import type { ReactNode } from 'react';
import type { StatusResponse } from '@0xsquid/sdk';
import { StatusResponse } from "@0xsquid/sdk";
/** implemented in useNotificationTypes */
export enum NotificationType {
@@ -19,31 +18,26 @@ export type NotificationTypeConfig<
/** React hook to trigger notifications based on app state */
useTrigger: (_: {
trigger: ({
id,
displayData,
updateKey,
isNew,
}: {
trigger: (
/** Unique ID for the triggered notification */
id: _NotificationId;
id: _NotificationId,
/** Display data for the triggered notification */
displayData: NotificationDisplayData;
displayData: NotificationDisplayData,
/**
* JSON-serializable key.
* Re-triggers the notification if passed a different value from the last trigger() call (even from a previous browser session).
* Suggested usage: data dependency array
*/
updateKey?: NotificationUpdateKey;
updateKey?: NotificationUpdateKey,
/**
* @param true (default): Notification initialized with status NotificationStatus.Triggered
* @param false: Notification initialized with status NotificationStatus.Cleared
*/
isNew?: boolean;
}) => void;
isNew?: boolean
) => void;
lastUpdated: number;
}) => void;
@@ -85,15 +79,15 @@ export type Notifications = Record<NotificationId, Notification<any>>;
/** Notification display data derived from app state at runtime. */
export type NotificationDisplayData = {
icon?: React.ReactElement<any, 'svg'>;
icon?: React.ReactNode;
title?: string;
description?: ReactNode;
description?: React.ReactNode;
customContent?: ReactNode;
customContent?: React.ReactNode;
customMenuContent?: ReactNode;
customMenuContent?: React.ReactNode;
actionDescription?: string;
+1 -1
View File
@@ -4,7 +4,7 @@ import { useMatch, useNavigate } from 'react-router-dom';
import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_MARKETID } from '@/constants/markets';
import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes';
import { AppRoute } from '@/constants/routes';
import { getSelectedNetwork } from '@/state/appSelectors';
import { closeDialogInTradeBox } from '@/state/dialogs';
+39 -38
View File
@@ -1,27 +1,27 @@
import { useCallback, useEffect, useMemo } from 'react';
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
import { groupBy } from 'lodash';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
import { AbacusOrderStatus, ORDER_SIDES, ORDER_STATUS_STRINGS } from '@/constants/abacus';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { type NotificationTypeConfig, NotificationType } from '@/constants/notifications';
import { ORDER_SIDE_STRINGS } from '@/constants/trade';
import { ORDER_SIDE_STRINGS, TRADE_TYPE_STRINGS, TradeTypes } from '@/constants/trade';
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
import { AssetIcon } from '@/components/AssetIcon';
import { Icon, IconName } from '@/components/Icon';
import { Output, OutputType } from '@/components/Output';
import { OrderStatusIcon } from '@/views/OrderStatusIcon';
import { TransferStatusToast } from '@/views/TransferStatus';
import { TransferStatusSteps } from '@/views/TransferStatusSteps';
import { getSubaccountFills, getSubaccountOrders } from '@/state/accountSelectors';
import { openDialog } from '@/state/dialogs';
import { OrderStatusIcon } from '@/views/OrderStatusIcon';
import { useStringGetter } from './useStringGetter';
import { TransferStatusSteps } from '@/views/TransferStatusSteps';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
export const notificationTypes = [
{
@@ -58,33 +58,34 @@ export const notificationTypes = [
}
: undefined);
if (order) console.log(order);
trigger({
id: order.id,
displayData: {
icon: <AssetIcon symbol={order.marketId.split('-')[0]} />, //<OrderStatusIcon status={order.status} totalFilled={order.totalFilled ?? 0} />,
title: `${stringGetter({
key: order.resources.typeStringKey ?? '',
})} ${
order.status === AbacusOrderStatus.open && (order?.totalFilled ?? 0) > 0
? stringGetter({ key: STRING_KEYS.PARTIALLY_FILLED })
: stringGetter({ key: ORDER_STATUS_STRINGS[order.status.name] })
}`,
description: `${stringGetter({
key: ORDER_SIDE_STRINGS[ORDER_SIDES[order.side.name]],
})} ${order.size} ${order.marketId} @ $${order.price}`,
actionDescription: 'View Order',
actionAltText: 'View this order in the Orders tab or the Notifications menu.',
toastSensitivity:
order.status === AbacusOrderStatus.pending ? 'foreground' : 'background',
toastDuration: Infinity,
},
updateKey: [order.status.name, order.size],
isNew: !order.createdAtMilliseconds || order.createdAtMilliseconds > lastUpdated,
});
if (order)
trigger(
order.id,
{
icon: (
<OrderStatusIcon status={order.status} totalFilled={order.totalFilled ?? 0} />
),
title: `${stringGetter({
key: TRADE_TYPE_STRINGS[order.type.rawValue as TradeTypes].tradeTypeKey,
})} ${
order.status === AbacusOrderStatus.open && (order?.totalFilled ?? 0) > 0
? stringGetter({ key: STRING_KEYS.PARTIALLY_FILLED })
: stringGetter({ key: ORDER_STATUS_STRINGS[order.status.name] })
}`,
description: `${stringGetter({
key: ORDER_SIDE_STRINGS[ORDER_SIDES[order.side.name]],
})} ${order.size} ${order.marketId} @ $${order.price}`,
actionDescription: 'View Order',
actionAltText: 'View this order in the Orders tab or the Notifications menu.',
toastSensitivity:
order.status === AbacusOrderStatus.pending ? 'foreground' : 'background',
toastDuration: 5000,
},
[order.status.name, order.size],
!order.createdAtMilliseconds || order.createdAtMilliseconds > lastUpdated
);
}
}, [orderIds, stringGetter]);
}, [orderIds]);
},
useNotificationAction: () => {
@@ -119,9 +120,9 @@ export const notificationTypes = [
const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
const type = toChainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdraw';
trigger({
id: txHash,
displayData: {
trigger(
txHash,
{
icon: <Icon iconName={finished ? IconName.Transfer : IconName.Clock} />,
title: stringGetter({ key: getTitleStringKey(type, finished) }),
// TODO: confirm with design what the description should be
@@ -141,10 +142,10 @@ export const notificationTypes = [
customMenuContent: !finished && <TransferStatusSteps status={transfer.status} />,
toastSensitivity: 'foreground',
},
updateKey: [],
});
[]
);
}
}, [transferNotifications, stringGetter]);
}, [transferNotifications]);
},
} as NotificationTypeConfig<string, []>,
},
] satisfies NotificationTypeConfig[];
+22 -17
View File
@@ -92,7 +92,7 @@ const useNotificationsContext = () => {
for (const { type, useTrigger } of notificationTypes)
useTrigger({
trigger: useCallback(
({ id, displayData, updateKey, isNew = true }) => {
(id, displayData, updateKey, isNew = true) => {
const key = getKey({ type, id });
const notification = notifications[key];
@@ -182,23 +182,28 @@ const useNotificationsContext = () => {
const iconUrl =
displayData.icon && (await renderSvgToDataUrl(displayData.icon).catch(() => undefined));
if (displayData.title) {
const pushNotification = new globalThis.Notification(displayData.title, {
renotify: true,
tag: getKey(notification),
data: notification,
body: displayData.description ?? '',
icon: (iconUrl as string) ?? '/favicon.svg',
badge: (iconUrl as string) ?? '/favicon.svg',
image: (iconUrl as string) ?? '/favicon.svg',
requireInteraction: displayData.toastDuration === Infinity,
});
const pushNotification = new globalThis.Notification(displayData.title, {
renotify: true,
tag: getKey(notification),
data: notification,
body: displayData.description,
icon: iconUrl ?? '/favicon.svg',
badge: iconUrl ?? '/favicon.svg',
image: iconUrl ?? '/favicon.svg',
vibrate: displayData.toastSensitivity === 'foreground',
requireInteraction: displayData.toastDuration === Infinity,
// actions: [
// {
// action: displayData.actionDescription,
// title: displayData.actionDescription,
// }
// ].slice(0, globalThis.Notification.maxActions),
});
pushNotification.addEventListener('click', () => {
onNotificationAction(notification);
markSeen(notification);
});
}
pushNotification.addEventListener('click', () => {
onNotificationAction(notification);
markSeen(notification);
});
}
setPushNotificationsLastUpdated(Date.now());
+1 -2
View File
@@ -36,7 +36,6 @@ import AbacusStateNotifier from './stateNotification';
import AbacusLocalizer from './localizer';
import AbacusFormatter from './formatter';
import AbacusThreading from './threading';
import AbacusTracking from './tracking';
import AbacusFileSystem, { ENDPOINTS_PATH } from './filesystem';
import { LocaleSeparators } from '../numbers';
class AbacusStateManager {
@@ -60,7 +59,7 @@ class AbacusStateManager {
new AbacusRest(),
this.websocket,
new AbacusChainTransaction(),
new AbacusTracking(),
null,
new AbacusThreading(),
new CoroutineTimer(),
new AbacusFileSystem()
-9
View File
@@ -1,9 +0,0 @@
import type { AbacusTrackingProtocol, Nullable } from '@/constants/abacus';
class AbacusTracking implements AbacusTrackingProtocol {
log(event: string, data: Nullable<string>) {
console.log({ event, data });
}
}
export default AbacusTracking;
+2 -4
View File
@@ -21,10 +21,8 @@ const applyComputedStyles = (html: string) => {
const toDataUrl = (bytes: string, type = 'image/svg+xml') =>
new Promise<string | ArrayBuffer | null>((resolve, reject) => {
Object.assign(new FileReader(), {
onload: (e: {
target: { result: string | ArrayBuffer | PromiseLike<string | ArrayBuffer | null> | null };
}) => resolve(e.target.result),
onerror: (e: { target: { error: Error } }) => reject(e.target.error),
onload: (e) => resolve(e.target.result),
onerror: (e) => reject(e.target.error),
}).readAsDataURL(new File([bytes], '', { type }));
});
+26 -22
View File
@@ -1,38 +1,42 @@
/*
* Legal Disclaimer for Web licence
/**
* @license
*
* Licence to use self-hosted webfonts for displaying dynamic text on specified website domains. Our package includes WOFF and WOFF2 font formats.
* Font Family: Satoshi
* Designed by: Deni Anggara
* URL: https://www.fontshare.com/fonts/satoshi
* © 2023 Indian Type Foundry
*
* Should the license page views limit be exceeded the license holder will be required to contact Lineto.com for an upgrade.
*
* It is strictly prohibited to rename the font and to download or use these fonts in any other media.
*
* These Web fonts are licensed exclusively for the use on the following domain(s) and their subdomains:
*
* dydx.exchange (monthly pageviews: <50K)
*
*/
* Font Styles:
* Satoshi Regular
* Satoshi Medium
* Satoshi Bold
* Satoshi Black
*/
@font-face {
src: url("fonts/CircularXXWeb-Book.woff2") format("woff2");
font-family: "Circular";
font-weight: 450;
src: url("fonts/Satoshi-Regular.woff2") format("woff2");
font-family: "Satoshi";
font-weight: 400;
}
@font-face {
src: url("fonts/CircularXXWeb-Medium.woff2") format("woff2");
font-family: "Circular";
src: url("fonts/Satoshi-Medium.woff2") format("woff2");
font-family: "Satoshi";
font-weight: 500;
}
@font-face {
src: url("fonts/CircularXXWeb-Bold.woff2") format("woff2");
font-family: "Circular";
src: url("fonts/Satoshi-Bold.woff2") format("woff2");
font-family: "Satoshi";
font-weight: 700;
}
@font-face {
src: url("fonts/CircularXXMonoWeb-Regular.woff2") format("woff2");
font-family: "CircularMono";
font-weight: 400;
src: url("fonts/Satoshi-Black.woff2") format("woff2");
font-family: "Satoshi";
font-weight: 900;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -8
View File
@@ -1,22 +1,16 @@
/* Constants */
:root {
--fontFamily-base: "Circular", system-ui, -apple-system, Helvetica, Arial, sans-serif;
--fontFamily-monospace: "CircularMono", Courier, monospace, var(--fontFamily-base);
--fontFamily-base: "Satoshi", system-ui, -apple-system, Helvetica, Arial, sans-serif;
--fontFamily-monospace: Courier, monospace, var(--fontFamily-base);
--fontWeight-base-0: 450;
--fontWeight-base-1: 500;
--fontWeight-base-2: 700;
--fontWeight-monospace: 400;
--letter-spacing-base: -0.02em;
/* New Typography system */
/**
* @size 10px/11px
* @weight 400
*/
--fontSize-tiny: 0.625rem; /* 10px/11px */
--fontSize-mini: 0.75rem; /* 12px/13px */
--fontSize-small: 0.8438rem; /* 13.5px/14.5px */
+22 -39
View File
@@ -2,7 +2,7 @@ import { shallowEqual, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent } from 'styled-components';
import { ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { STRING_KEYS, type StringKey } from '@/constants/localization';
import { useBreakpoints, useStringGetter } from '@/hooks';
import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins';
@@ -34,10 +34,7 @@ export const MarketDetails: React.FC = () => {
initialMarginFraction,
maintenanceMarginFraction,
minOrderSize,
maxPositionSize,
baselinePositionSize,
incrementalInitialMarginFraction,
incrementalPositionSize,
stepSizeDecimals,
tickSizeDecimals,
} = configs;
@@ -72,12 +69,28 @@ export const MarketDetails: React.FC = () => {
key: 'step-size',
label: stringGetter({ key: STRING_KEYS.STEP_SIZE }),
tooltip: 'step-size',
value: <Output useGrouping value={stepSize} type={OutputType.Asset} tag={symbol} />,
value: (
<Output
useGrouping
value={stepSize}
type={OutputType.Asset}
tag={symbol}
fractionDigits={stepSizeDecimals}
/>
),
},
{
key: 'min-order-size',
label: stringGetter({ key: STRING_KEYS.MINIMUM_ORDER_SIZE }),
value: <Output useGrouping value={minOrderSize} type={OutputType.Asset} tag={symbol} />,
value: (
<Output
useGrouping
value={minOrderSize}
type={OutputType.Asset}
tag={symbol}
fractionDigits={stepSizeDecimals}
/>
),
},
{
key: 'max-leverage',
@@ -105,36 +118,6 @@ export const MarketDetails: React.FC = () => {
tooltip: 'initial-margin-fraction',
value: <Output useGrouping value={initialMarginFraction} type={OutputType.SmallPercent} />,
},
{
key: 'incremental-initial-margin-fraction',
label: stringGetter({ key: STRING_KEYS.INCREMENTAL_INITIAL_MARGIN_FRACTION }),
value: (
<Output
useGrouping
value={incrementalInitialMarginFraction}
type={OutputType.SmallPercent}
/>
),
},
{
key: 'incremental-position-size',
label: stringGetter({ key: STRING_KEYS.INCREMENTAL_POSITION_SIZE }),
value: (
<Output useGrouping value={incrementalPositionSize} type={OutputType.Asset} tag={symbol} />
),
},
{
key: 'baseline-position-size',
label: stringGetter({ key: STRING_KEYS.BASELINE_POSITION_SIZE }),
value: (
<Output useGrouping value={baselinePositionSize} type={OutputType.Asset} tag={symbol} />
),
},
{
key: 'max-position-size',
label: stringGetter({ key: STRING_KEYS.MAXIMUM_POSITION_SIZE }),
value: <Output useGrouping value={maxPositionSize} type={OutputType.Asset} tag={symbol} />,
},
];
return (
@@ -150,10 +133,10 @@ export const MarketDetails: React.FC = () => {
<Styled.MarketDescription>
{primaryDescriptionKey && (
<p>{stringGetter({ key: STRING_KEYS[primaryDescriptionKey] })}</p>
<p>{stringGetter({ key: STRING_KEYS[primaryDescriptionKey as StringKey] })}</p>
)}
{secondaryDescriptionKey && (
<p>{stringGetter({ key: STRING_KEYS[secondaryDescriptionKey] })}</p>
<p>{stringGetter({ key: STRING_KEYS[secondaryDescriptionKey as StringKey] })}</p>
)}
</Styled.MarketDescription>
@@ -7,7 +7,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { AbacusOrderStatus, AbacusOrderTypes, type Nullable } from '@/constants/abacus';
import { ButtonAction } from '@/constants/buttons';
import { STRING_KEYS, StringKey } from '@/constants/localization';
import { STRING_KEYS } from '@/constants/localization';
import { AssetIcon } from '@/components/AssetIcon';
import { Button } from '@/components/Button';
@@ -38,9 +38,6 @@ type ElementProps = {
setIsOpen: (open: boolean) => void;
};
const isStringKey = (key: Nullable<string>): key is StringKey =>
key != null && STRING_KEYS.hasOwnProperty(key);
export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const dispatch = useDispatch();
@@ -50,7 +47,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
const { cancelOrder } = useSubaccount();
const {
asset,
asset = {},
cancelReason,
createdAtMilliseconds,
expiresAtMilliseconds,
@@ -60,7 +57,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
price,
reduceOnly,
totalFilled,
resources,
resources = {},
size,
status,
stepSizeDecimals,
@@ -112,9 +109,9 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
<Styled.Row>
<Styled.StatusIcon iconName={statusIcon} color={statusIconColor} />
<Styled.Status>
{isStringKey(statusStringKey)
{statusStringKey
? stringGetter({ key: statusStringKey })
: isStringKey(resources.statusStringKey)
: resources.statusStringKey
? stringGetter({ key: resources.statusStringKey })
: undefined}
</Styled.Status>
@@ -124,9 +121,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
{
key: 'cancel-reason',
label: stringGetter({ key: STRING_KEYS.CANCEL_REASON }),
value: isStringKey(cancelReason)
? stringGetter({ key: STRING_KEYS[cancelReason] })
: undefined,
value: cancelReason ? stringGetter({ key: STRING_KEYS[cancelReason] }) : undefined,
},
{
key: 'amount',
+8 -10
View File
@@ -1,12 +1,10 @@
import React, { useMemo } from 'react';
import { groupBy } from 'lodash';
import styled from 'styled-components';
import { STRING_KEYS } from '@/constants/localization';
import { groupBy } from 'lodash';
import { type Notification, NotificationStatus } from '@/constants/notifications';
import { useStringGetter } from '@/hooks';
import { useNotifications } from '@/hooks/useNotifications';
import { CloseIcon } from '@/icons';
import { Button } from '@/components/Button';
import { ButtonAction, ButtonSize } from '@/constants/buttons';
@@ -15,7 +13,9 @@ import { DialogPlacement } from '@/components/Dialog';
import { Output, OutputType } from '@/components/Output';
import { IconButton } from '@/components/IconButton';
import { Toolbar } from '@/components/Toolbar';
import { CloseIcon } from '@/icons';
import styled from 'styled-components';
import { layoutMixins } from '@/styles/layoutMixins';
type ElementProps = {
@@ -27,8 +27,6 @@ export const NotificationsMenu = ({
slotTrigger,
placement = DialogPlacement.Sidebar,
}: ElementProps) => {
const stringGetter = useStringGetter();
const {
notifications,
getDisplayData,
@@ -71,7 +69,7 @@ export const NotificationsMenu = ({
.map(([status, notifications]) => ({
group: status,
groupLabel: {
[NotificationStatus.Triggered]: stringGetter({ key: STRING_KEYS.NEW }),
[NotificationStatus.Triggered]: 'New',
// [NotificationStatus.Updated]: 'Updates',
[NotificationStatus.Seen]: 'Seen',
[NotificationStatus.Cleared]: 'Archived',
@@ -137,7 +135,7 @@ export const NotificationsMenu = ({
isOpen={isMenuOpen || placement === DialogPlacement.Inline}
setIsOpen={setIsMenuOpen}
items={items}
title={stringGetter({ key: STRING_KEYS.NOTIFICATIONS })}
title="Notifications"
slotTrigger={
<$TriggerContainer>
{slotTrigger}
@@ -170,7 +168,7 @@ export const NotificationsMenu = ({
),
}}
>
{stringGetter({ key: STRING_KEYS.CLEAR })}
Clear All
</Button>
</$FooterToolbar>
}