feat: ledger entries (#1944)

This commit is contained in:
Art 2022-11-04 11:47:39 +01:00 committed by GitHub
parent 750d6a31eb
commit 28b38c3ebf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 7301 additions and 0 deletions

View File

@ -12,6 +12,7 @@ import { ResizableGrid } from '@vegaprotocol/ui-toolkit';
import { LayoutPriority } from 'allotment';
import { usePageTitleStore } from '../../stores';
import { AccountsContainer } from './accounts-container';
import { LedgerContainer } from '@vegaprotocol/ledger';
const Portfolio = () => {
const { updateTitle } = usePageTitleStore((store) => ({
@ -42,6 +43,11 @@ const Portfolio = () => {
<FillsContainer />
</VegaWalletContainer>
</Tab>
<Tab id="ledger-entries" name={t('Ledger entries')}>
<VegaWalletContainer>
<LedgerContainer />
</VegaWalletContainer>
</Tab>
</Tabs>
</PortfolioGridChild>
</ResizableGridPanel>

12
libs/ledger/.babelrc Normal file
View File

@ -0,0 +1,12 @@
{
"presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}

View File

@ -0,0 +1,18 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

11
libs/ledger/README.md Normal file
View File

@ -0,0 +1,11 @@
# ledger
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test ledger` to execute the unit tests via [Jest](https://jestjs.io).
## Running lint
Run `nx lint ledger` to execute the lint via [ESLint](https://eslint.org/).

View File

@ -0,0 +1,15 @@
/* eslint-disable */
export default {
displayName: 'ledger',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/ledger',
};

4
libs/ledger/package.json Normal file
View File

@ -0,0 +1,4 @@
{
"name": "@vegaprotocol/ledger",
"version": "0.0.1"
}

43
libs/ledger/project.json Normal file
View File

@ -0,0 +1,43 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/ledger/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nrwl/web:rollup",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/ledger",
"tsConfig": "libs/ledger/tsconfig.lib.json",
"project": "libs/ledger/package.json",
"entryFile": "libs/ledger/src/index.ts",
"external": ["react/jsx-runtime"],
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
"compiler": "babel",
"assets": [
{
"glob": "libs/ledger/README.md",
"input": ".",
"output": "."
}
]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/ledger/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/libs/ledger"],
"options": {
"jestConfig": "libs/ledger/jest.config.ts",
"passWithNoTests": true
}
}
},
"tags": []
}

2
libs/ledger/src/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './lib/ledger-container';
export * from './lib/ledger-manager';

View File

@ -0,0 +1,26 @@
fragment LedgerEntry on AggregatedLedgerEntries {
vegaTime
quantity
partyId
assetId
marketId
accountType
transferType
}
query LedgerEntries($partyId: ID!) {
ledgerEntries(
filter: { AccountFromFilter: { partyIds: [$partyId] } }
groupOptions: {
ByAccountField: [PartyId, AccountType, AssetId, MarketId]
ByLedgerEntryField: [TransferType]
}
pagination: { first: 500 }
) {
edges {
node {
...LedgerEntry
}
}
}
}

View File

@ -0,0 +1,68 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type LedgerEntryFragment = { __typename?: 'AggregatedLedgerEntries', vegaTime: string, quantity: string, partyId?: string | null, assetId?: string | null, marketId?: string | null, accountType?: Types.AccountType | null, transferType?: string | null };
export type LedgerEntriesQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type LedgerEntriesQuery = { __typename?: 'Query', ledgerEntries: { __typename?: 'AggregatedLedgerEntriesConnection', edges: Array<{ __typename?: 'AggregatedLedgerEntriesEdge', node: { __typename?: 'AggregatedLedgerEntries', vegaTime: string, quantity: string, partyId?: string | null, assetId?: string | null, marketId?: string | null, accountType?: Types.AccountType | null, transferType?: string | null } } | null> } };
export const LedgerEntryFragmentDoc = gql`
fragment LedgerEntry on AggregatedLedgerEntries {
vegaTime
quantity
partyId
assetId
marketId
accountType
transferType
}
`;
export const LedgerEntriesDocument = gql`
query LedgerEntries($partyId: ID!) {
ledgerEntries(
filter: {AccountFromFilter: {partyIds: [$partyId]}}
groupOptions: {ByAccountField: [PartyId, AccountType, AssetId, MarketId], ByLedgerEntryField: [TransferType]}
pagination: {first: 500}
) {
edges {
node {
...LedgerEntry
}
}
}
}
${LedgerEntryFragmentDoc}`;
/**
* __useLedgerEntriesQuery__
*
* To run a query within a React component, call `useLedgerEntriesQuery` and pass it any options that fit your needs.
* When your component renders, `useLedgerEntriesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useLedgerEntriesQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function useLedgerEntriesQuery(baseOptions: Apollo.QueryHookOptions<LedgerEntriesQuery, LedgerEntriesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<LedgerEntriesQuery, LedgerEntriesQueryVariables>(LedgerEntriesDocument, options);
}
export function useLedgerEntriesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<LedgerEntriesQuery, LedgerEntriesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<LedgerEntriesQuery, LedgerEntriesQueryVariables>(LedgerEntriesDocument, options);
}
export type LedgerEntriesQueryHookResult = ReturnType<typeof useLedgerEntriesQuery>;
export type LedgerEntriesLazyQueryHookResult = ReturnType<typeof useLedgerEntriesLazyQuery>;
export type LedgerEntriesQueryResult = Apollo.QueryResult<LedgerEntriesQuery, LedgerEntriesQueryVariables>;

View File

@ -0,0 +1,17 @@
import { t } from '@vegaprotocol/react-helpers';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { LedgerManager } from './ledger-manager';
export const LedgerContainer = () => {
const { pubKey } = useVegaWallet();
if (!pubKey) {
return (
<Splash>
<p>{t('Please connect Vega wallet')}</p>
</Splash>
);
}
return <LedgerManager partyId={pubKey} />;
};

View File

@ -0,0 +1,57 @@
import type { Asset } from '@vegaprotocol/assets';
import { assetsProvider } from '@vegaprotocol/assets';
import type { Market } from '@vegaprotocol/market-list';
import { marketsProvider } from '@vegaprotocol/market-list';
import {
makeDataProvider,
makeDerivedDataProvider,
useDataProvider,
} from '@vegaprotocol/react-helpers';
import { useMemo } from 'react';
import type {
LedgerEntriesQuery,
LedgerEntryFragment,
} from './__generated___/LedgerEntries';
import { LedgerEntriesDocument } from './__generated___/LedgerEntries';
export type LedgerEntry = LedgerEntryFragment & {
id: number;
asset: Asset | null | undefined;
market: Market | null | undefined;
};
const getData = (responseData: LedgerEntriesQuery): LedgerEntry[] =>
responseData.ledgerEntries?.edges
?.filter((e) => Boolean(e?.node))
.map((e, i) => ({ id: i, ...e?.node } as LedgerEntry)) ?? [];
const ledgerEntriesOnlyProvider = makeDataProvider<
LedgerEntriesQuery,
LedgerEntry[] | null,
never,
never
>({ query: LedgerEntriesDocument, getData });
export const ledgerEntriesProvider = makeDerivedDataProvider<
LedgerEntry[],
never
>(
[ledgerEntriesOnlyProvider, assetsProvider, marketsProvider],
([entries, assets, markets]): LedgerEntry[] =>
entries.map((entry: LedgerEntry) => {
const asset = assets.find((asset: Asset) => asset.id === entry.assetId);
const market = markets.find(
(market: Market) => market.id === entry.marketId
);
return { ...entry, asset, market };
})
);
export const useLedgerEntriesDataProvider = (partyId: string) => {
const variables = useMemo(() => ({ partyId }), [partyId]);
return useDataProvider({
dataProvider: ledgerEntriesProvider,
variables,
skip: !partyId,
});
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useLedgerEntriesDataProvider } from './ledger-entries-data-provider';
import { LedgerTable } from './ledger-table';
// '3ac37999796c2be3546e0c1d87daa8ec7e99d8c423969be44c2f63256c415004'
type LedgerManagerProps = { partyId: string };
export const LedgerManager = ({ partyId }: LedgerManagerProps) => {
const { data, error, loading } = useLedgerEntriesDataProvider(partyId);
return (
<AsyncRenderer data={data} error={error} loading={loading}>
<LedgerTable rowData={data} />
</AsyncRenderer>
);
};

View File

@ -0,0 +1,94 @@
import {
addDecimalsFormatNumber,
fromNanoSeconds,
getDateTimeFormat,
t,
} from '@vegaprotocol/react-helpers';
import type { VegaValueFormatterParams } from '@vegaprotocol/ui-toolkit';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react';
import { AccountTypeMapping, TransferTypeMapping } from '@vegaprotocol/types';
import type { LedgerEntry } from './ledger-entries-data-provider';
export const LedgerTable = ({ ...props }) => (
<AgGrid
style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No entries')}
rowHeight={34}
getRowId={({ data }) => data.id}
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
filter: true,
}}
{...props}
>
<AgGridColumn
headerName={t('Account Type')}
field="accountType"
valueFormatter={({
value,
}: VegaValueFormatterParams<LedgerEntry, 'accountType'>) =>
value
? AccountTypeMapping[value as keyof typeof AccountTypeMapping]
: ''
}
/>
<AgGridColumn
headerName={t('Transfer Type')}
field="transferType"
valueFormatter={({
value,
}: VegaValueFormatterParams<LedgerEntry, 'transferType'>) =>
value
? TransferTypeMapping[value as keyof typeof TransferTypeMapping]
: ''
}
/>
<AgGridColumn
headerName={t('Quantity')}
field="quantity"
valueFormatter={({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'quantity'>) => {
const marketDecimalPlaces = data?.market?.decimalPlaces;
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(
value,
assetDecimalPlaces,
marketDecimalPlaces
)
: value;
}}
/>
<AgGridColumn
headerName={t('Asset')}
field="assetId"
valueFormatter={({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'asset'>) =>
data?.asset?.symbol || value
}
/>
<AgGridColumn
headerName={t('Market')}
field="marketId"
valueFormatter={({
data,
}: VegaValueFormatterParams<LedgerEntry, 'market'>) =>
data?.market?.tradableInstrument.instrument.code || '-'
}
/>
<AgGridColumn
headerName={t('Vega Time')}
field="vegaTime"
valueFormatter={({ value }: { value: string }) =>
getDateTimeFormat().format(fromNanoSeconds(value))
}
/>
</AgGrid>
);

28
libs/ledger/tsconfig.json Normal file
View File

@ -0,0 +1,28 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./.storybook/tsconfig.json"
}
]
}

View File

@ -0,0 +1,27 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.stories.ts",
"**/*.stories.js",
"**/*.stories.jsx",
"**/*.stories.tsx",
"jest.config.ts"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

View File

@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node", "@testing-library/jest-dom"]
},
"include": [
"**/*.test.ts",
"**/*.spec.ts",
"**/*.test.tsx",
"**/*.spec.tsx",
"**/*.test.js",
"**/*.spec.js",
"**/*.test.jsx",
"**/*.spec.jsx",
"**/*.d.ts",
"jest.config.ts"
]
}

View File

@ -1,3 +1,9 @@
export const toNanoSeconds = (date: Date | string) => {
return new Date(date).getTime().toString() + '000000';
};
export const fromNanoSeconds = (ts: string) => {
if (typeof ts !== 'string') return new Date(0);
const validTs = ts.substring(0, ts.length - 6);
return new Date(Number(validTs));
};

View File

@ -294,3 +294,33 @@ export enum ProposalUserAction {
CREATE = 'CREATE',
VOTE = 'VOTE',
}
// https://docs.vega.xyz/testnet/api/grpc/vega/vega.proto#transfertype
export enum TransferTypeMapping {
TRANSFER_TYPE_UNSPECIFIED = 'Default value, always invalid',
TRANSFER_TYPE_LOSS = 'Loss',
TRANSFER_TYPE_WIN = 'Win',
TRANSFER_TYPE_CLOSE = 'Close',
TRANSFER_TYPE_MTM_LOSS = 'Mark to market loss',
TRANSFER_TYPE_MTM_WIN = 'Mark to market win',
TRANSFER_TYPE_MARGIN_LOW = 'Margin too low',
TRANSFER_TYPE_MARGIN_HIGH = 'Margin too high',
TRANSFER_TYPE_MARGIN_CONFISCATED = 'Margin was confiscated',
TRANSFER_TYPE_MAKER_FEE_PAY = 'Pay maker fee',
TRANSFER_TYPE_MAKER_FEE_RECEIVE = 'Receive maker fee',
TRANSFER_TYPE_INFRASTRUCTURE_FEE_PAY = 'Pay infrastructure fee',
TRANSFER_TYPE_INFRASTRUCTURE_FEE_DISTRIBUTE = 'Receive infrastructure fee',
TRANSFER_TYPE_LIQUIDITY_FEE_PAY = 'Pay liquidity fee',
TRANSFER_TYPE_LIQUIDITY_FEE_DISTRIBUTE = 'Receive liquidity fee',
TRANSFER_TYPE_BOND_LOW = 'Bond too low',
TRANSFER_TYPE_BOND_HIGH = 'Bond too high',
TRANSFER_TYPE_WITHDRAW_LOCK = 'Lock amount for withdraw',
TRANSFER_TYPE_WITHDRAW = 'Actual withdraw from system',
TRANSFER_TYPE_DEPOSIT = 'Deposit funds',
TRANSFER_TYPE_BOND_SLASHING = 'Bond slashing',
TRANSFER_TYPE_STAKE_REWARD = 'Stake reward',
TRANSFER_TYPE_TRANSFER_FUNDS_SEND = 'Transfer funds',
TRANSFER_TYPE_TRANSFER_FUNDS_DISTRIBUTE = 'Transfer funds',
TRANSFER_TYPE_CLEAR_ACCOUNT = 'Market is closed, accounts are cleared',
TRANSFER_TYPE_CHECKPOINT_BALANCE_RESTORE = 'Restore a balance from a checkpoint',
}

View File

@ -25,6 +25,7 @@
"@vegaprotocol/environment": ["libs/environment/src/index.ts"],
"@vegaprotocol/fills": ["libs/fills/src/index.ts"],
"@vegaprotocol/governance": ["libs/governance/src/index.ts"],
"@vegaprotocol/ledger": ["libs/ledger/src/index.ts"],
"@vegaprotocol/liquidity": ["libs/liquidity/src/index.ts"],
"@vegaprotocol/market-depth": ["libs/market-depth/src/index.ts"],
"@vegaprotocol/market-info": ["libs/market-info/src/index.ts"],

View File

@ -14,6 +14,7 @@
"explorer-e2e": "apps/explorer-e2e",
"fills": "libs/fills",
"governance": "libs/governance",
"ledger": "libs/ledger",
"liquidity": "libs/liquidity",
"liquidity-provision-dashboard": "apps/liquidity-provision-dashboard",
"liquidity-provision-dashboard-e2e": "apps/liquidity-provision-dashboard-e2e",