Basic NIS dashboard (#9437)
This commit is contained in:
parent
e198af942a
commit
93c7c0e8e5
@ -10,6 +10,7 @@ Contributed:
|
||||
Changes:
|
||||
|
||||
- Cater for current Substrate bonding extrinsic arguments
|
||||
- Add basic NIS dashboard
|
||||
- Disable unreachable endpoints
|
||||
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ import gilt from './gilt.js';
|
||||
import js from './js.js';
|
||||
import membership from './membership.js';
|
||||
import nfts from './nfts.js';
|
||||
import nis from './nis.js';
|
||||
import parachains from './parachains.js';
|
||||
import poll from './poll.js';
|
||||
import preimages from './preimages.js';
|
||||
@ -74,10 +75,11 @@ export default function create (t: TFunction): Routes {
|
||||
bounties(t),
|
||||
// others
|
||||
parachains(t),
|
||||
gilt(t),
|
||||
assets(t),
|
||||
nfts(t),
|
||||
society(t),
|
||||
nis(t),
|
||||
gilt(t),
|
||||
scheduler(t),
|
||||
calendar(t),
|
||||
contracts(t),
|
||||
|
||||
23
packages/apps-routing/src/nis.ts
Normal file
23
packages/apps-routing/src/nis.ts
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2017-2023 @polkadot/apps-routing authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { Route } from './types.js';
|
||||
|
||||
import Component from '@polkadot/app-nis';
|
||||
|
||||
export default function create (t: TFunction): Route {
|
||||
return {
|
||||
Component,
|
||||
display: {
|
||||
needsApi: [
|
||||
'tx.nis.placeBid',
|
||||
'query.proxy.proxies'
|
||||
]
|
||||
},
|
||||
group: 'network',
|
||||
icon: 'leaf',
|
||||
name: 'nis',
|
||||
text: t<string>('nav.nis', 'Non-interactive Staking', { ns: 'apps-routing' })
|
||||
};
|
||||
}
|
||||
0
packages/page-nis/.skip-build
Normal file
0
packages/page-nis/.skip-build
Normal file
0
packages/page-nis/.skip-npm
Normal file
0
packages/page-nis/.skip-npm
Normal file
1
packages/page-nis/README.md
Normal file
1
packages/page-nis/README.md
Normal file
@ -0,0 +1 @@
|
||||
# @polkadot/app-nis
|
||||
26
packages/page-nis/package.json
Normal file
26
packages/page-nis/package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"bugs": "https://github.com/polkadot-js/apps/issues",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/apps/tree/master/packages/page-nis#readme",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@polkadot/app-nis",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"directory": "packages/page-nis",
|
||||
"type": "git",
|
||||
"url": "https://github.com/polkadot-js/apps.git"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "0.130.2-1-x",
|
||||
"dependencies": {
|
||||
"@polkadot/react-components": "^0.130.2-1-x"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-dom": "*",
|
||||
"react-is": "*"
|
||||
}
|
||||
}
|
||||
132
packages/page-nis/src/Overview/BidAdd.tsx
Normal file
132
packages/page-nis/src/Overview/BidAdd.tsx
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@polkadot/util';
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { Button, InputAddress, InputBalance, InputNumber, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useApi, useToggle } from '@polkadot/react-hooks';
|
||||
import { Available } from '@polkadot/react-query';
|
||||
import { BN_ONE } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
proxies: Record<string, string[]>;
|
||||
}
|
||||
|
||||
function Bid ({ className, isDisabled, proxies }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [isOpen, toggleOpen] = useToggle();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [proxyId, setProxyId] = useState<string | null>(null);
|
||||
const [amount, setAmount] = useState<BN | undefined>();
|
||||
const [duration, setDuration] = useState<BN | undefined>();
|
||||
|
||||
const tx = useMemo(
|
||||
() => accountId && amount && duration
|
||||
? api.tx.proxy.proxy(accountId, null, api.tx.nis.placeBid(amount, duration))
|
||||
: null,
|
||||
[api, accountId, amount, duration]
|
||||
);
|
||||
|
||||
const proxiedAccounts = Object.keys(proxies);
|
||||
const isAmountError = !amount || amount.isZero() || amount.lt(api.consts.nis.minBid);
|
||||
const isDurationError = !duration || !duration.gte(BN_ONE) || duration.gt(api.consts.nis.queueCount);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon='plus'
|
||||
isDisabled={!proxiedAccounts.length || isDisabled}
|
||||
label={t<string>('Bid via Proxy')}
|
||||
onClick={toggleOpen}
|
||||
/>
|
||||
{isOpen && (
|
||||
<Modal
|
||||
className={className}
|
||||
header={t<string>('submit nis bid')}
|
||||
onClose={toggleOpen}
|
||||
size='large'
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Columns hint={t<string>('This account will make the bid for the nis and pay all associated fees.')}>
|
||||
<InputAddress
|
||||
filter={proxiedAccounts}
|
||||
label={t<string>('use proxied account')}
|
||||
labelExtra={
|
||||
<Available
|
||||
label={<span className='label'>{t<string>('transferrable')}</span>}
|
||||
params={accountId}
|
||||
/>
|
||||
}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
/>
|
||||
{accountId && (
|
||||
<InputAddress
|
||||
filter={proxies[accountId]}
|
||||
label={t<string>('send via proxy')}
|
||||
onChange={setProxyId}
|
||||
type='account'
|
||||
/>
|
||||
)}
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The amount you wish to lock for the duration. It needs to be more than the nis minimum.')}>
|
||||
<InputBalance
|
||||
autoFocus
|
||||
defaultValue={api.consts.nis.minBid}
|
||||
isError={isAmountError}
|
||||
isZeroable={false}
|
||||
label={t<string>('bid amount')}
|
||||
onChange={setAmount}
|
||||
/>
|
||||
<InputBalance
|
||||
defaultValue={api.consts.nis.minBid}
|
||||
isDisabled
|
||||
label={t<string>('minimum bid amount')}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The number of periods this bid is to be locked for, less than the maximum period.')}>
|
||||
<InputNumber
|
||||
defaultValue={BN_ONE}
|
||||
isError={isDurationError}
|
||||
isZeroable={false}
|
||||
label={t<string>('lock periods')}
|
||||
onChange={setDuration}
|
||||
/>
|
||||
<InputNumber
|
||||
defaultValue={api.consts.nis.queueCount}
|
||||
isDisabled
|
||||
label={t<string>('maximum lock periods')}
|
||||
/>
|
||||
{!isDurationError && (
|
||||
<InputNumber
|
||||
defaultValue={api.consts.nis.basePeriod.mul(duration)}
|
||||
isDisabled
|
||||
label={t<string>('length of lock (blocks, calculated)')}
|
||||
/>
|
||||
)}
|
||||
</Modal.Columns>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<TxButton
|
||||
accountId={proxyId}
|
||||
extrinsic={tx}
|
||||
icon='check'
|
||||
isDisabled={isAmountError || isDurationError || !accountId}
|
||||
label={t<string>('Bid')}
|
||||
onStart={toggleOpen}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Bid);
|
||||
31
packages/page-nis/src/Overview/Queue.tsx
Normal file
31
packages/page-nis/src/Overview/Queue.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { QueueTotal } from './types.js';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
value: QueueTotal;
|
||||
}
|
||||
|
||||
function Queue ({ className, value: { balance, index, numItems } }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<tr className={className}>
|
||||
<Table.Column.Id value={index} />
|
||||
<td className='number all'>
|
||||
{formatNumber(numItems)}
|
||||
</td>
|
||||
<td className='number'>
|
||||
<FormatBalance value={balance} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Queue);
|
||||
43
packages/page-nis/src/Overview/Queues.tsx
Normal file
43
packages/page-nis/src/Overview/Queues.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { QueueTotal } from './types.js';
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate.js';
|
||||
import Queue from './Queue.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
queueTotals?: QueueTotal[];
|
||||
}
|
||||
|
||||
function Queues ({ className, queueTotals }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
|
||||
[t<string>('queues'), 'start'],
|
||||
[t<string>('participants'), 'number'],
|
||||
[t<string>('balance'), 'number']
|
||||
]);
|
||||
|
||||
return (
|
||||
<Table
|
||||
className={className}
|
||||
empty={queueTotals && t<string>('No active nis queues found.')}
|
||||
header={headerRef.current}
|
||||
>
|
||||
{queueTotals?.map((value) => (
|
||||
<Queue
|
||||
key={value.index}
|
||||
value={value}
|
||||
/>
|
||||
))}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Queues);
|
||||
55
packages/page-nis/src/Overview/Summary.tsx
Normal file
55
packages/page-nis/src/Overview/Summary.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { NisInfo } from './types.js';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { CardSummary, SummaryBox } from '@polkadot/react-components';
|
||||
import { useApi, useBestNumber } from '@polkadot/react-hooks';
|
||||
import { BN_QUINTILL } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
info?: NisInfo;
|
||||
isDisabled?: boolean;
|
||||
}
|
||||
|
||||
function Summary ({ className, info }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const bestNumber = useBestNumber();
|
||||
|
||||
return (
|
||||
<SummaryBox className={className}>
|
||||
<section>
|
||||
{bestNumber && (
|
||||
<CardSummary
|
||||
label={t<string>('intake')}
|
||||
progress={{
|
||||
total: api.consts.nis.intakePeriod,
|
||||
value: bestNumber.mod(api.consts.nis.intakePeriod),
|
||||
withTime: true
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
{info?.summary && (
|
||||
<CardSummary
|
||||
label={t<string>('proportion')}
|
||||
progress={{
|
||||
isPercent: true,
|
||||
total: BN_QUINTILL,
|
||||
value: info.summary.proportionOwed
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
</SummaryBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Summary);
|
||||
41
packages/page-nis/src/Overview/index.tsx
Normal file
41
packages/page-nis/src/Overview/index.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Button } from '@polkadot/react-components';
|
||||
|
||||
import { useProxies } from '../useProxies.js';
|
||||
import BidAdd from './BidAdd.js';
|
||||
import Queues from './Queues.js';
|
||||
import Summary from './Summary.js';
|
||||
import useInfo from './useInfo.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function Overview ({ className }: Props): React.ReactElement<Props> {
|
||||
const proxies = useProxies();
|
||||
const { info } = useInfo();
|
||||
|
||||
const isDisabled = !info || !info.summary || info.summary.proportionOwed.isZero();
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary
|
||||
info={info}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
<Button.Group>
|
||||
<BidAdd
|
||||
isDisabled
|
||||
proxies={proxies}
|
||||
/>
|
||||
</Button.Group>
|
||||
<Queues queueTotals={info?.queueTotals} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Overview);
|
||||
16
packages/page-nis/src/Overview/types.ts
Normal file
16
packages/page-nis/src/Overview/types.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { u32, u128 } from '@polkadot/types';
|
||||
import type { PalletNisSummaryRecord } from '@polkadot/types/lookup';
|
||||
|
||||
export interface QueueTotal {
|
||||
balance: u128;
|
||||
index: number;
|
||||
numItems: u32;
|
||||
}
|
||||
|
||||
export interface NisInfo {
|
||||
queueTotals?: QueueTotal[];
|
||||
summary?: PalletNisSummaryRecord;
|
||||
}
|
||||
39
packages/page-nis/src/Overview/useInfo.ts
Normal file
39
packages/page-nis/src/Overview/useInfo.ts
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { u32, u128 } from '@polkadot/types';
|
||||
import type { PalletNisSummaryRecord } from '@polkadot/types/lookup';
|
||||
import type { NisInfo, QueueTotal } from './types.js';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { createNamedHook, useApi, useCallMulti } from '@polkadot/react-hooks';
|
||||
|
||||
interface State {
|
||||
info?: NisInfo;
|
||||
}
|
||||
|
||||
const OPT_GILT = {
|
||||
defaultValue: {} as NisInfo,
|
||||
transform: ([summary, queueTotals]: [PalletNisSummaryRecord, [u32, u128][]]): NisInfo => ({
|
||||
queueTotals: queueTotals
|
||||
.map(([numItems, balance], index): QueueTotal => ({ balance, index: index + 1, numItems }))
|
||||
.filter(({ balance }) => !balance.isZero()),
|
||||
summary
|
||||
})
|
||||
};
|
||||
|
||||
function useInfoImpl (): State {
|
||||
const { api } = useApi();
|
||||
const info = useCallMulti<NisInfo>([
|
||||
api.query.nis.summary,
|
||||
api.query.nis.queueTotals
|
||||
], OPT_GILT);
|
||||
|
||||
return useMemo(
|
||||
() => ({ info }),
|
||||
[info]
|
||||
);
|
||||
}
|
||||
|
||||
export default createNamedHook('useInfo', useInfoImpl);
|
||||
48
packages/page-nis/src/index.tsx
Normal file
48
packages/page-nis/src/index.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
import { Route, Routes } from 'react-router';
|
||||
|
||||
import { Tabs } from '@polkadot/react-components';
|
||||
|
||||
import Overview from './Overview/index.js';
|
||||
import { useTranslation } from './translate.js';
|
||||
|
||||
interface Props {
|
||||
basePath: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function NisApp ({ basePath, className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const tabsRef = useRef([
|
||||
{
|
||||
isRoot: true,
|
||||
name: 'overview',
|
||||
text: t<string>('Overview')
|
||||
}
|
||||
]);
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={tabsRef.current}
|
||||
/>
|
||||
<Routes>
|
||||
<Route path={basePath}>
|
||||
<Route
|
||||
element={
|
||||
<Overview />
|
||||
}
|
||||
index
|
||||
/>
|
||||
</Route>
|
||||
</Routes>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(NisApp);
|
||||
10
packages/page-nis/src/translate.ts
Normal file
10
packages/page-nis/src/translate.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { UseTranslationResponse } from 'react-i18next';
|
||||
|
||||
import { useTranslation as useTranslationBase } from 'react-i18next';
|
||||
|
||||
export function useTranslation (): UseTranslationResponse<'app-nis', undefined> {
|
||||
return useTranslationBase('app-nis');
|
||||
}
|
||||
42
packages/page-nis/src/useProxies.ts
Normal file
42
packages/page-nis/src/useProxies.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2017-2023 @polkadot/app-nis authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Vec } from '@polkadot/types';
|
||||
import type { BalanceOf, ProxyDefinition } from '@polkadot/types/interfaces';
|
||||
import type { ITuple } from '@polkadot/types/types';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { createNamedHook, useAccounts, useApi, useIsMountedRef } from '@polkadot/react-hooks';
|
||||
|
||||
type ProxyResult = ITuple<[Vec<ProxyDefinition>, BalanceOf]>;
|
||||
|
||||
function useProxiesImpl (): Record<string, string[]> {
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const mountedRef = useIsMountedRef();
|
||||
const [state, setState] = useState<Record<string, string[]>>({});
|
||||
|
||||
useEffect((): void => {
|
||||
if (allAccounts.length) {
|
||||
api.query.proxy.proxies
|
||||
.multi<ProxyResult>(allAccounts)
|
||||
.then((result) =>
|
||||
mountedRef.current && setState(
|
||||
result
|
||||
.map(([p], index): [string, string[]] => [
|
||||
allAccounts[index],
|
||||
p.map(({ delegate }) => delegate.toString())
|
||||
])
|
||||
.filter(([, p]) => p.length)
|
||||
.reduce((all, [a, p]) => ({ ...all, [a]: p }), {})
|
||||
)
|
||||
)
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [allAccounts, api, mountedRef]);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export const useProxies = createNamedHook('useProxies', useProxiesImpl);
|
||||
9
packages/page-nis/tsconfig.build.json
Normal file
9
packages/page-nis/tsconfig.build.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "..",
|
||||
"outDir": "./build",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
@ -39,6 +39,7 @@
|
||||
"@polkadot/app-js": ["page-js/src/index.tsx"],
|
||||
"@polkadot/app-membership": ["page-membership/src/index.tsx"],
|
||||
"@polkadot/app-nfts": ["page-nfts/src/index.tsx"],
|
||||
"@polkadot/app-nis": ["page-nis/src/index.tsx"],
|
||||
"@polkadot/app-parachains": ["page-parachains/src/index.tsx"],
|
||||
"@polkadot/app-parachains/Teleport": ["page-parachains/src/Teleport.tsx"],
|
||||
"@polkadot/app-parachains/useLeaseRanges": ["page-parachains/src/useLeaseRanges.ts"],
|
||||
|
||||
14
yarn.lock
14
yarn.lock
@ -1574,6 +1574,18 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@polkadot/app-nis@workspace:packages/page-nis":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@polkadot/app-nis@workspace:packages/page-nis"
|
||||
dependencies:
|
||||
"@polkadot/react-components": ^0.130.2-1-x
|
||||
peerDependencies:
|
||||
react: "*"
|
||||
react-dom: "*"
|
||||
react-is: "*"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@polkadot/app-parachains@workspace:packages/page-parachains":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@polkadot/app-parachains@workspace:packages/page-parachains"
|
||||
@ -2142,7 +2154,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@polkadot/react-components@^0.130.2-4-x, @polkadot/react-components@workspace:packages/react-components":
|
||||
"@polkadot/react-components@^0.130.2-1-x, @polkadot/react-components@^0.130.2-4-x, @polkadot/react-components@workspace:packages/react-components":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@polkadot/react-components@workspace:packages/react-components"
|
||||
dependencies:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user