Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nabla/feat export csv fix #7

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
81eb640
feat: :heavy_plus_sign: add export-to-csv dep
DavideSegullo Feb 28, 2024
9ef18f9
fix: :bug: fix post merge errors
DavideSegullo May 21, 2024
0158bb5
feat: :sparkles: add reusable format number function
DavideSegullo May 21, 2024
819984e
refactor: :recycle: add better style declaration
DavideSegullo May 22, 2024
41da261
fix: :bug: fix post merge errors
DavideSegullo Jun 14, 2024
d6ac3f8
fix: :bug: fix react errors
DavideSegullo Jun 14, 2024
4f6d079
fix: :bug: fix empty export
DavideSegullo Jun 14, 2024
860387c
feat: :arrow_up: upgrade @dydxprotocol/v4-client-js
DavideSegullo Jun 14, 2024
a662c0c
feat: :sparkles: add history export all services
DavideSegullo Jun 14, 2024
909da9d
feat: :sparkles: add export loading states
DavideSegullo Jun 14, 2024
e04e9fb
refactor: :art: improve format of the code
DavideSegullo Jun 17, 2024
3346b79
style: :rotating_light: fix lint errors
DavideSegullo Jun 17, 2024
dd063b1
style: :rotating_light: add default case
DavideSegullo Jun 17, 2024
0142267
feat: :sparkles: add export sorting in decreasing order
DavideSegullo Jun 17, 2024
6d84dcf
feat: :sparkles: add price column inside csv
DavideSegullo Jun 18, 2024
acd86a5
feat: :arrow_up: upgrade @dydxprotocol/v4-client-js
DavideSegullo Jun 21, 2024
f1be5fe
feat: :sparkles: add parentSubaccount export
DavideSegullo Jun 21, 2024
1b29b36
feat: :sparkles: disable export on disconnection state
DavideSegullo Jun 21, 2024
9d9e088
docs: :memo: improve comment format
DavideSegullo Jun 21, 2024
173eeb3
fix: :bug: fix merge error
DavideSegullo Jun 21, 2024
9713a8f
style: :rotating_light: fix lint errors
DavideSegullo Jun 21, 2024
69e0700
feat: :sparkles: add new analytics api
DavideSegullo Jun 26, 2024
3e1fb9c
feat: :sparkles: replace formatNumber with formatNumberOutput
DavideSegullo Jun 28, 2024
169ea9a
Merge branch 'main' into nabla/feat-export_csv_fix
DavideSegullo Jun 28, 2024
899c91d
chore: :rotating_light: fix lint errors
DavideSegullo Jun 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"cosmjs-types": "^0.9.0",
"crypto-js": "^4.1.1",
"ethers": "^6.6.1",
"export-to-csv": "^1.2.3",
"graz": "^0.0.43",
"jsdom": "^24.1.0",
"lodash": "^4.17.21",
Expand Down
9 changes: 8 additions & 1 deletion pnpm-lock.yaml

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

26 changes: 21 additions & 5 deletions src/components/DropdownMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { Fragment, type Ref } from 'react';

import { Content, Item, Portal, Root, Separator, Trigger } from '@radix-ui/react-dropdown-menu';
import {
Content,
Item,
Portal,
Root,
Separator,
Trigger,
type DropdownMenuProps as RadixDropdownMenuProps,
type DropdownMenuTriggerProps as RadixDropdownMenuTriggerProps,
} from '@radix-ui/react-dropdown-menu';
import styled from 'styled-components';

import { popoverMixins } from '@/styles/popoverMixins';
Expand All @@ -13,7 +22,7 @@ export type DropdownMenuItem<T> = {
value: T;
icon?: React.ReactNode;
label: React.ReactNode;
onSelect?: () => void;
onSelect?: (e: Event) => void;
separator?: boolean;
highlightColor?: 'accent' | 'create' | 'destroy';
};
Expand All @@ -32,7 +41,11 @@ type ElementProps<T> = {
slotTopContent?: React.ReactNode;
};

type DropdownMenuProps<T> = StyleProps & ElementProps<T>;
export type DropdownMenuProps<T> = StyleProps &
ElementProps<T> &
RadixDropdownMenuProps & {
triggerOptions?: RadixDropdownMenuTriggerProps;
};

export const DropdownMenu = forwardRefFn(
<T extends string>(
Expand All @@ -45,12 +58,14 @@ export const DropdownMenu = forwardRefFn(
slotTopContent,
side = 'bottom',
sideOffset = 8,
triggerOptions,
...rest
}: DropdownMenuProps<T>,
ref: Ref<HTMLButtonElement>
) => {
return (
<Root>
<$Trigger ref={ref} className={className}>
<Root {...rest}>
<$Trigger ref={ref} className={className} {...triggerOptions}>
{children}
<$DropdownIcon aria-hidden="true">
<Icon iconName={IconName.Triangle} aria-hidden="true" />
Expand Down Expand Up @@ -79,6 +94,7 @@ export const DropdownMenu = forwardRefFn(
);
}
);

const $Separator = styled(Separator)`
border-bottom: solid var(--border-width) var(--color-border);
margin: 0.25rem 1rem;
Expand Down
4 changes: 2 additions & 2 deletions src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export enum IconName {
Deposit = 'Deposit',
DepthChart = 'DepthChart',
Discord = 'Discord',
Download = 'Download',
Etherscan = 'Etherscan',
ExportKeys = 'ExportKeys',
Feedback = 'Feedback',
Expand Down Expand Up @@ -173,7 +174,6 @@ export enum IconName {
Website = 'Website',
Whitepaper = 'Whitepaper',
Withdraw = 'Withdraw',
Download = 'Download',
SocialX = 'SocialX',
}

Expand Down Expand Up @@ -205,6 +205,7 @@ const icons = {
[IconName.Deposit]: DepositIcon,
[IconName.DepthChart]: DepthChartIcon,
[IconName.Discord]: DiscordIcon,
[IconName.Download]: DownloadIcon,
[IconName.Etherscan]: EtherscanIcon,
[IconName.ExportKeys]: ExportKeysIcon,
[IconName.Feedback]: FeedbackIcon,
Expand Down Expand Up @@ -260,7 +261,6 @@ const icons = {
[IconName.Website]: WebsiteIcon,
[IconName.Whitepaper]: WhitepaperIcon,
[IconName.Withdraw]: WithdrawIcon,
[IconName.Download]: DownloadIcon,
[IconName.SocialX]: SocialXIcon,
} as Record<IconName, ElementType>;

Expand Down
15 changes: 15 additions & 0 deletions src/components/NavigationMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ import { Tag } from './Tag';
type ElementProps<MenuItemValue extends string, MenuGroupValue extends string> = {
items: MenuConfig<MenuItemValue, MenuGroupValue>;
onSelectItem?: (value: MenuItemValue) => void;
onSelectGroup?: (value: MenuGroupValue) => void;
/**
* Optional slot to add content before the menu item rendering area
*/
slotBefore?: React.ReactNode;
/**
* Optional slot to add content after the menu item rendering area
*/
slotAfter?: React.ReactNode;
};

type StyleProps = {
Expand Down Expand Up @@ -104,6 +113,8 @@ export const NavigationMenu = <MenuItemValue extends string, MenuGroupValue exte
itemOrientation = 'horizontal',
submenuPlacement = 'inline', // orientation === 'horizontal' ? 'viewport' : 'inline',
dir = 'ltr',
slotAfter,
slotBefore,
className,
}: ElementProps<MenuItemValue, MenuGroupValue> & StyleProps) => {
const renderSubitems = ({
Expand Down Expand Up @@ -148,6 +159,8 @@ export const NavigationMenu = <MenuItemValue extends string, MenuGroupValue exte

return (
<$Root orientation={orientation} dir={dir} className={className}>
{slotBefore}

{items.map((group) => (
<$Group key={group.group}>
{group.groupLabel && (
Expand All @@ -171,6 +184,8 @@ export const NavigationMenu = <MenuItemValue extends string, MenuGroupValue exte
))}

{submenuPlacement === 'viewport' && <$Viewport data-orientation={orientation} />}

{slotAfter}
</$Root>
);
};
Expand Down
34 changes: 34 additions & 0 deletions src/constants/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,37 @@ export const AMOUNT_RESERVED_FOR_GAS_DYDX = 0.1;
* @description The number of parentSubaccounts: 0 - 127, 128 is the first childSubaccount
*/
export const NUM_PARENT_SUBACCOUNTS = 128;

export type RawSubaccountFill = {
clientMetadata: string;
createdAt: string;
createdAtHeight: string;
fee: string;
id: string;
liquidity: string;
market: string;
marketType: string;
orderId: string;
price: string;
side: string;
size: string;
subaccountNumber: number;
type: string;
};

export type RawSubaccountTransfer = {
id: string;
sender: RawAccount;
recipient: RawAccount;
size: string;
createdAt: string;
createdAtHeight: string;
symbol: string;
type: string;
transactionHash: string;
};

export type RawAccount = {
address: string;
subaccountNumber: number;
};
13 changes: 13 additions & 0 deletions src/constants/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ export const AnalyticsEvents = unionize(
trailingBlocks?: number;
}>(),

// Export CSV
ExportCsvClick: ofType<{}>(),
ExportDownloadClick: ofType<{
trades: boolean;
transfers: boolean;
}>(),
ExportTradesCheckboxClick: ofType<{
value: boolean;
}>(),
ExportTransfersCheckboxClick: ofType<{
value: boolean;
}>(),

// Navigation
NavigatePage: ofType<{
path: string;
Expand Down
106 changes: 106 additions & 0 deletions src/hooks/useDydxClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import type { ResolutionString } from 'public/tradingview/charting_library';

import type { ConnectNetworkEvent, NetworkConfig } from '@/constants/abacus';
import { RawSubaccountFill, RawSubaccountTransfer } from '@/constants/account';
import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics';
import { RESOLUTION_MAP, type Candle } from '@/constants/candles';
import { LocalStorageKey } from '@/constants/localStorage';
Expand Down Expand Up @@ -166,6 +167,109 @@ const useDydxClientContext = () => {
}
};

const requestAllAccountFills = async (address: string, subaccountNumber: number) => {
try {
const {
fills = [],
totalResults,
pageSize,
} = await indexerClient.account.getParentSubaccountNumberFills(
address,
subaccountNumber,
undefined,
undefined,
100,
undefined,
undefined,
1
);

// We get all the pages but we should exclude the first one, we already have this data
const pages = Array.from(
{
length: Math.ceil(totalResults / pageSize) - 1,
},
(_, index) => index + 2
);

const results = await Promise.all(
pages.map((page) =>
indexerClient.account.getParentSubaccountNumberFills(
address,
subaccountNumber,
undefined,
undefined,
100,
undefined,
undefined,
page
)
)
);

const allFills: RawSubaccountFill[] = [...fills, ...results.map((data) => data.fills).flat()];

// sorts the data in descending order
return allFills.sort((fillA, fillB) => {
return new Date(fillB.createdAt).getTime() - new Date(fillA.createdAt).getTime();
});
} catch (error) {
log('useDydxClient/requestAllAccountFills', error);
return [];
}
};

const requestAllAccountTransfers = async (address: string, subaccountNumber: number) => {
try {
const {
transfers = [],
totalResults,
pageSize,
} = await indexerClient.account.getParentSubaccountNumberTransfers(
address,
subaccountNumber,
100,
undefined,
undefined,
1
);

// We get all the pages but we should exclude the first one, we already have this data
const pages = Array.from(
{
length: Math.ceil(totalResults / pageSize) - 1,
},
(_, index) => index + 2
);

const results = await Promise.all(
pages.map((page) =>
indexerClient.account.getParentSubaccountNumberTransfers(
address,
subaccountNumber,
100,
undefined,
undefined,
page
)
)
);

const allTransfers: RawSubaccountTransfer[] = [
...transfers,
...results.map((data) => data.transfers).flat(),
];

// sorts the data in descending order
return allTransfers.sort((transferA, transferB) => {
return new Date(transferB.createdAt).getTime() - new Date(transferA.createdAt).getTime();
});
} catch (error) {
log('useDydxClient/requestAllAccountTransfers', error);
return [];
}
};

const getMarketTickSize = async (marketId: string) => {
try {
const { markets } = (await indexerClient.markets.getPerpetualMarkets(marketId)) ?? {};
Expand Down Expand Up @@ -324,6 +428,8 @@ const useDydxClientContext = () => {
getWalletFromEvmSignature,

// Public Methods
requestAllAccountTransfers,
requestAllAccountFills,
requestAllPerpetualMarkets,
requestAllGovernanceProposals,
getCandlesForDatafeed,
Expand Down
15 changes: 15 additions & 0 deletions src/lib/csv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ConfigOptions, download, generateCsv, mkConfig } from 'export-to-csv';

export const exportCSV = <T extends object>(data: T[], options: ConfigOptions = {}) => {
const { filename = 'generated', showColumnHeaders = true, ...rest } = options;
const config = mkConfig({ showColumnHeaders, filename, ...rest });

const csv = generateCsv(config)(
data as {
[k: string]: unknown;
[k: number]: unknown;
}[]
);

download(config)(csv);
};
Loading
Loading