Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf391bd5ef | ||
|
|
d77a8792fc | ||
|
|
64adba4eec |
+2
-1
@@ -59,4 +59,5 @@ apps/trading/e2e/logs/
|
||||
apps/trading/e2e/.pytest_cache/
|
||||
apps/trading/e2e/traces/
|
||||
|
||||
.nx/
|
||||
.nx/cache
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export const FundingContainer = ({ marketId }: { marketId: string }) => {
|
||||
if (edge.node.endTime) {
|
||||
acc?.push({
|
||||
endTime: fromNanoSeconds(edge.node.endTime),
|
||||
fundingRate: Number(edge.node.fundingRate),
|
||||
fundingRate: Number(edge.node.fundingRate) * 100,
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
@@ -82,8 +82,7 @@ export const FundingContainer = ({ marketId }: { marketId: string }) => {
|
||||
<LineChart
|
||||
data={values}
|
||||
theme={theme}
|
||||
priceFormat={(fundingRate) => `${(fundingRate * 100).toFixed(4)}%`}
|
||||
yAxisTickFormat="%"
|
||||
priceFormat={(fundingRate) => `${fundingRate.toFixed(4)}%`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ def test_transfer_submit(continuous_market, vega: VegaService, page: Page):
|
||||
page.select_option('[data-testid=transfer-form] [name="toVegaKey"]', index=2)
|
||||
page.select_option('[data-testid=transfer-form] [name="fromAccount"]', index=1)
|
||||
|
||||
expected_asset_text = re.compile(r"tDAI tDAI999991.49731 tDAI.{6}….{4}")
|
||||
expected_asset_text = re.compile(r"tDAI tDAI999,991.49731 tDAI.{6}….{4}")
|
||||
actual_asset_text = page.get_by_test_id('select-asset').text_content().strip()
|
||||
|
||||
assert expected_asset_text.search(actual_asset_text), f"Expected pattern not found in {actual_asset_text}"
|
||||
@@ -42,7 +42,7 @@ def test_transfer_submit(continuous_market, vega: VegaService, page: Page):
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
expected_confirmation_text = re.compile(r"Transfer completeYour transaction has been confirmedView in block explorerTransferTo .{6}….{6}1\.00 tDAI")
|
||||
expected_confirmation_text = re.compile(r"Transfer completeYour transaction has been confirmed View in block explorerTransferTo .{6}….{6}1\.00 tDAI")
|
||||
actual_confirmation_text = page.get_by_test_id('toast-content').text_content()
|
||||
assert expected_confirmation_text.search(actual_confirmation_text), f"Expected pattern not found in {actual_confirmation_text}"
|
||||
|
||||
@@ -127,6 +127,6 @@ def test_transfer_vesting_below_minimum(continuous_market, vega: VegaService, pa
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
expected_confirmation_text = re.compile(r"Transfer completeYour transaction has been confirmedView in block explorerTransferTo .{6}….{6}0\.00001 tDAI")
|
||||
expected_confirmation_text = re.compile(r"Transfer completeYour transaction has been confirmed View in block explorerTransferTo .{6}….{6}0\.00001 tDAI")
|
||||
actual_confirmation_text = page.get_by_test_id('toast-content').text_content()
|
||||
assert expected_confirmation_text.search(actual_confirmation_text), f"Expected pattern not found in {actual_confirmation_text}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { getAsset, getQuoteName } from '@vegaprotocol/markets';
|
||||
import { getAsset, getProductType, getQuoteName } from '@vegaprotocol/markets';
|
||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
|
||||
@@ -297,120 +297,134 @@ export const DealTicketMarginDetails = ({
|
||||
);
|
||||
|
||||
const quoteName = getQuoteName(market);
|
||||
const productType = getProductType(market);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-2 pt-2">
|
||||
<Accordion>
|
||||
<AccordionPanel
|
||||
itemId="margin"
|
||||
trigger={
|
||||
<AccordionPrimitive.Trigger
|
||||
data-testid="accordion-toggle"
|
||||
className={classNames(
|
||||
'w-full',
|
||||
'flex items-center gap-2 text-xs',
|
||||
'group'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
data-testid={`deal-ticket-fee-margin-required`}
|
||||
key={'value-dropdown'}
|
||||
className="flex items-center justify-between w-full gap-2"
|
||||
>
|
||||
<div className="flex items-center text-left gap-1">
|
||||
<Tooltip
|
||||
description={t(
|
||||
'MARGIN_DIFF_TOOLTIP_TEXT',
|
||||
MARGIN_DIFF_TOOLTIP_TEXT,
|
||||
{ assetSymbol }
|
||||
)}
|
||||
>
|
||||
<span className="text-muted">{t('Margin required')}</span>
|
||||
</Tooltip>
|
||||
{/*
|
||||
TODO: remove this conditional check once the following PRs are deployed
|
||||
and the estimatePosition query is working for perps
|
||||
|
||||
<AccordionChevron size={10} />
|
||||
</div>
|
||||
<Tooltip
|
||||
description={
|
||||
formatRange(
|
||||
marginRequiredBestCase,
|
||||
marginRequiredWorstCase,
|
||||
assetDecimals
|
||||
) ?? '-'
|
||||
}
|
||||
- https://github.com/vegaprotocol/vega/pull/10119
|
||||
- https://github.com/vegaprotocol/vega/pull/10122
|
||||
*/}
|
||||
{productType === 'Future' && (
|
||||
<>
|
||||
<Accordion>
|
||||
<AccordionPanel
|
||||
itemId="margin"
|
||||
trigger={
|
||||
<AccordionPrimitive.Trigger
|
||||
data-testid="accordion-toggle"
|
||||
className={classNames(
|
||||
'w-full',
|
||||
'flex items-center gap-2 text-xs',
|
||||
'group'
|
||||
)}
|
||||
>
|
||||
<div className="font-mono text-right">
|
||||
{formatValue(
|
||||
marginRequiredWorstCase,
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}{' '}
|
||||
{assetSymbol || ''}
|
||||
<div
|
||||
data-testid={`deal-ticket-fee-margin-required`}
|
||||
key={'value-dropdown'}
|
||||
className="flex items-center justify-between w-full gap-2"
|
||||
>
|
||||
<div className="flex items-center text-left gap-1">
|
||||
<Tooltip
|
||||
description={t(
|
||||
'MARGIN_DIFF_TOOLTIP_TEXT',
|
||||
MARGIN_DIFF_TOOLTIP_TEXT,
|
||||
{ assetSymbol }
|
||||
)}
|
||||
>
|
||||
<span className="text-muted">
|
||||
{t('Margin required')}
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
<AccordionChevron size={10} />
|
||||
</div>
|
||||
<Tooltip
|
||||
description={
|
||||
formatRange(
|
||||
marginRequiredBestCase,
|
||||
marginRequiredWorstCase,
|
||||
assetDecimals
|
||||
) ?? '-'
|
||||
}
|
||||
>
|
||||
<div className="font-mono text-right">
|
||||
{formatValue(
|
||||
marginRequiredWorstCase,
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}{' '}
|
||||
{assetSymbol || ''}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</AccordionPrimitive.Trigger>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<KeyValue
|
||||
label={t('Total margin available')}
|
||||
indent
|
||||
value={formatValue(totalMarginAvailable, assetDecimals)}
|
||||
formattedValue={formatValue(
|
||||
totalMarginAvailable,
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={t(
|
||||
'TOTAL_MARGIN_AVAILABLE',
|
||||
TOTAL_MARGIN_AVAILABLE,
|
||||
{
|
||||
generalAccountBalance: formatValue(
|
||||
generalAccountBalance,
|
||||
</AccordionPrimitive.Trigger>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<KeyValue
|
||||
label={t('Total margin available')}
|
||||
indent
|
||||
value={formatValue(totalMarginAvailable, assetDecimals)}
|
||||
formattedValue={formatValue(
|
||||
totalMarginAvailable,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
marginAccountBalance: formatValue(
|
||||
)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={t(
|
||||
'TOTAL_MARGIN_AVAILABLE',
|
||||
TOTAL_MARGIN_AVAILABLE,
|
||||
{
|
||||
generalAccountBalance: formatValue(
|
||||
generalAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
marginAccountBalance: formatValue(
|
||||
marginAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
marginMaintenance: formatValue(
|
||||
currentMargins?.maintenanceLevel,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
assetSymbol,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
{deductionFromCollateral}
|
||||
<KeyValue
|
||||
label={t('Current margin allocation')}
|
||||
indent
|
||||
onClick={
|
||||
generalAccountBalance
|
||||
? () => setBreakdownDialog(true)
|
||||
: undefined
|
||||
}
|
||||
value={formatValue(marginAccountBalance, assetDecimals)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={t(
|
||||
'MARGIN_ACCOUNT_TOOLTIP_TEXT',
|
||||
MARGIN_ACCOUNT_TOOLTIP_TEXT
|
||||
)}
|
||||
formattedValue={formatValue(
|
||||
marginAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
marginMaintenance: formatValue(
|
||||
currentMargins?.maintenanceLevel,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
assetSymbol,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
{deductionFromCollateral}
|
||||
<KeyValue
|
||||
label={t('Current margin allocation')}
|
||||
indent
|
||||
onClick={
|
||||
generalAccountBalance
|
||||
? () => setBreakdownDialog(true)
|
||||
: undefined
|
||||
}
|
||||
value={formatValue(marginAccountBalance, assetDecimals)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={t(
|
||||
'MARGIN_ACCOUNT_TOOLTIP_TEXT',
|
||||
MARGIN_ACCOUNT_TOOLTIP_TEXT
|
||||
)}
|
||||
formattedValue={formatValue(
|
||||
marginAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
{projectedMargin}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
{projectedMargin}
|
||||
</>
|
||||
)}
|
||||
<KeyValue
|
||||
label={t('Liquidation')}
|
||||
value={liquidationPriceEstimateRange}
|
||||
|
||||
@@ -6,17 +6,14 @@ import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MAXGOINT64 } from '@vegaprotocol/utils';
|
||||
|
||||
const mockCreate = jest.fn();
|
||||
|
||||
jest.mock('@vegaprotocol/wallet', () => ({
|
||||
...jest.requireActual('@vegaprotocol/wallet'),
|
||||
useVegaWallet: jest.fn(() => ({ pubKey: 'partyId' })),
|
||||
}));
|
||||
|
||||
jest.mock('@vegaprotocol/web3', () => ({
|
||||
...jest.requireActual('@vegaprotocol/web3'),
|
||||
useVegaTransactionStore: jest.fn(() => mockCreate),
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockUseDataProvider = (args: any) => {
|
||||
if (args.dataProvider === positionsMarketsProvider) {
|
||||
@@ -24,20 +21,17 @@ const mockUseDataProvider = (args: any) => {
|
||||
}
|
||||
return { data: [singleRow] };
|
||||
};
|
||||
|
||||
jest.mock('@vegaprotocol/data-provider', () => ({
|
||||
...jest.requireActual('@vegaprotocol/data-provider'),
|
||||
useDataProvider: jest.fn((args) => mockUseDataProvider(args)),
|
||||
}));
|
||||
|
||||
describe('PositionsManager', () => {
|
||||
// TODO: Close position temporarily disabled in https://github.com/vegaprotocol/frontend-monorepo/pull/5350
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('should close position with max uint64', async () => {
|
||||
it('should close position with max uint64', async () => {
|
||||
render(<PositionsManager partyIds={['partyId']} isReadOnly={false} />, {
|
||||
wrapper: MockedProvider,
|
||||
});
|
||||
expect(await screen.findByTestId('close-position')).toBeInTheDocument();
|
||||
expect(await screen.getByTestId('close-position')).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByTestId('close-position'));
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { PositionsTable } from './positions-table';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { useVegaTransactionStore } from '@vegaprotocol/web3';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import {
|
||||
@@ -6,15 +9,9 @@ import {
|
||||
positionsMarketsProvider,
|
||||
} from './positions-data-providers';
|
||||
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { MAXGOINT64 } from '@vegaprotocol/utils';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
// TODO: Close position temporarily disabled in https://github.com/vegaprotocol/frontend-monorepo/pull/5350
|
||||
//
|
||||
// import { useCallback } from 'react';
|
||||
// import * as Schema from '@vegaprotocol/types';
|
||||
// import { useVegaTransactionStore } from '@vegaprotocol/web3';
|
||||
// import { MAXGOINT64 } from '@vegaprotocol/utils';
|
||||
|
||||
interface PositionsManagerProps {
|
||||
partyIds: string[];
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
@@ -32,37 +29,33 @@ export const PositionsManager = ({
|
||||
}: PositionsManagerProps) => {
|
||||
const t = useT();
|
||||
const { pubKeys, pubKey } = useVegaWallet();
|
||||
|
||||
// TODO: Close position temporarily disabled in https://github.com/vegaprotocol/frontend-monorepo/pull/5350
|
||||
//
|
||||
// const create = useVegaTransactionStore((store) => store.create);
|
||||
//
|
||||
// const onClose = useCallback(
|
||||
// ({ marketId, openVolume }: { marketId: string; openVolume: string }) =>
|
||||
// create({
|
||||
// batchMarketInstructions: {
|
||||
// cancellations: [
|
||||
// {
|
||||
// marketId,
|
||||
// orderId: '', // omit order id to cancel all active orders
|
||||
// },
|
||||
// ],
|
||||
// submissions: [
|
||||
// {
|
||||
// marketId: marketId,
|
||||
// type: Schema.OrderType.TYPE_MARKET as const,
|
||||
// timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC as const,
|
||||
// side: openVolume.startsWith('-')
|
||||
// ? Schema.Side.SIDE_BUY
|
||||
// : Schema.Side.SIDE_SELL,
|
||||
// size: MAXGOINT64, // improvement for avoiding leftovers filled in the meantime when close request has been sent
|
||||
// reduceOnly: true,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// }),
|
||||
// [create]
|
||||
// );
|
||||
const create = useVegaTransactionStore((store) => store.create);
|
||||
const onClose = useCallback(
|
||||
({ marketId, openVolume }: { marketId: string; openVolume: string }) =>
|
||||
create({
|
||||
batchMarketInstructions: {
|
||||
cancellations: [
|
||||
{
|
||||
marketId,
|
||||
orderId: '', // omit order id to cancel all active orders
|
||||
},
|
||||
],
|
||||
submissions: [
|
||||
{
|
||||
marketId: marketId,
|
||||
type: Schema.OrderType.TYPE_MARKET as const,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC as const,
|
||||
side: openVolume.startsWith('-')
|
||||
? Schema.Side.SIDE_BUY
|
||||
: Schema.Side.SIDE_SELL,
|
||||
size: MAXGOINT64, // improvement for avoiding leftovers filled in the meantime when close request has been sent
|
||||
reduceOnly: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
[create]
|
||||
);
|
||||
|
||||
const { data: marketIds } = useDataProvider({
|
||||
dataProvider: positionsMarketsProvider,
|
||||
@@ -81,8 +74,7 @@ export const PositionsManager = ({
|
||||
pubKeys={pubKeys}
|
||||
rowData={data}
|
||||
onMarketClick={onMarketClick}
|
||||
// TODO: temporarily disable close position
|
||||
// onClose={onClose}
|
||||
onClose={onClose}
|
||||
isReadOnly={isReadOnly}
|
||||
multipleKeys={partyIds.length > 1}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No positions')}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useVegaWallet } from '../use-vega-wallet';
|
||||
import { useChainId } from './use-chain-id';
|
||||
|
||||
global.fetch = jest.fn();
|
||||
const mockFetch = global.fetch as jest.Mock;
|
||||
mockFetch.mockImplementation((url: string) => {
|
||||
return Promise.resolve({ ok: true });
|
||||
});
|
||||
|
||||
jest.mock('../use-vega-wallet', () => {
|
||||
const original = jest.requireActual('../use-vega-wallet');
|
||||
return {
|
||||
...original,
|
||||
useVegaWallet: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('useChainId', () => {
|
||||
it('does not call fetch when statistics url could not be determined', async () => {
|
||||
(useVegaWallet as jest.Mock).mockReturnValue({
|
||||
vegaUrl: '',
|
||||
});
|
||||
renderHook(() => useChainId());
|
||||
await waitFor(() => {
|
||||
expect(mockFetch).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls fetch with correct statistics url', async () => {
|
||||
(useVegaWallet as jest.Mock).mockReturnValue({
|
||||
vegaUrl: 'http://localhost:1234/graphql',
|
||||
});
|
||||
renderHook(() => useChainId());
|
||||
await waitFor(() => {
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
'http://localhost:1234/statistics'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not return chain id when chain id is not present in response', async () => {
|
||||
(useVegaWallet as jest.Mock).mockReturnValue({
|
||||
vegaUrl: 'http://localhost:1234/graphql',
|
||||
});
|
||||
const { result } = renderHook(() => useChainId());
|
||||
await waitFor(() => {
|
||||
expect(result.current).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns chain id when chain id is present in response', async () => {
|
||||
(useVegaWallet as jest.Mock).mockReturnValue({
|
||||
vegaUrl: 'http://localhost:1234/graphql',
|
||||
});
|
||||
mockFetch.mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => ({
|
||||
statistics: {
|
||||
chainId: '1234',
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
const { result } = renderHook(() => useChainId());
|
||||
await waitFor(() => {
|
||||
expect(result.current).not.toBeUndefined();
|
||||
expect(result.current).toEqual('1234');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,37 +3,17 @@ import { useVegaWallet } from '../use-vega-wallet';
|
||||
|
||||
const cache: Record<string, string> = {};
|
||||
|
||||
/**
|
||||
* Gets the statistics url for a given vega url.
|
||||
* Example:
|
||||
* https://graphql.example.com/graphql -> https://graphql.example.com/statistics
|
||||
*/
|
||||
const getNodeStatisticsUrl = (vegaUrl: string) => {
|
||||
try {
|
||||
const url = new URL(vegaUrl);
|
||||
url.pathname = 'statistics';
|
||||
return url.toString();
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const useChainId = () => {
|
||||
const { vegaUrl } = useVegaWallet();
|
||||
const [chainId, setChainId] = useState<undefined | string>(cache[vegaUrl]);
|
||||
const [fetchAttempt, setFetchAttempt] = useState(1);
|
||||
|
||||
const statisticsUrl = getNodeStatisticsUrl(vegaUrl);
|
||||
|
||||
useEffect(() => {
|
||||
// abort when `/statistics` URL could not be determined
|
||||
if (!statisticsUrl) return;
|
||||
let isCancelled = false;
|
||||
if (cache[vegaUrl]) {
|
||||
setChainId(cache[vegaUrl]);
|
||||
return;
|
||||
}
|
||||
fetch(statisticsUrl)
|
||||
fetch(vegaUrl.replace('graphql', 'statistics'))
|
||||
.then((response) => response.json())
|
||||
.then((response) => {
|
||||
if (isCancelled) {
|
||||
@@ -52,6 +32,6 @@ export const useChainId = () => {
|
||||
return () => {
|
||||
isCancelled = true;
|
||||
};
|
||||
}, [fetchAttempt, statisticsUrl, vegaUrl]);
|
||||
}, [fetchAttempt, vegaUrl]);
|
||||
return chainId;
|
||||
};
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@
|
||||
"jsondiffpatch": "^0.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "13.3.0",
|
||||
"pennant": "^1.15.0",
|
||||
"pennant": "^1.14.1",
|
||||
"react": "18.2.0",
|
||||
"react-copy-to-clipboard": "5.1.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
||||
@@ -17884,10 +17884,10 @@ pend@~1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
|
||||
|
||||
pennant@^1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/pennant/-/pennant-1.15.0.tgz#21854cf78466cbd27eda8143c21abcde070d4d76"
|
||||
integrity sha512-p3H4vu6BP7nUqn7s2pjyTl0FMJUT2U5lq5UKuxy55F2E66sJnvp5XFvNjRZvi42vyzsxG8WURgwQDQKNqQgWAw==
|
||||
pennant@^1.14.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/pennant/-/pennant-1.14.1.tgz#8e7a53256095e398b03397af31c831b02a56032b"
|
||||
integrity sha512-rjzo/tlFanO96OKhJiyjQtjug7sY6pjVZqVbieD3Tf4zt20bqIb6m4L2JfwOXEe0jqP/yddgCNPY+vlkVuJW1Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@d3fc/d3fc-technical-indicator" "^8.0.1"
|
||||
|
||||
Reference in New Issue
Block a user