Compare commits

...

3 Commits

Author SHA1 Message Date
asiaznik
2846d3e0e3
chore(governance): lp votes for batch proposal 2024-03-11 13:15:25 +01:00
Matthew Russell
05b39e2c08
fix(trading): usdt approvals (#5939) 2024-03-07 14:21:31 +00:00
Matthew Russell
654dd1e7b0
fix(trading): stored state causing wrong chart data (#5928) 2024-03-05 18:17:18 +00:00
15 changed files with 171 additions and 72 deletions

View File

@ -38,6 +38,7 @@ import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats'; import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
import { MarketName } from '../proposal/market-name'; import { MarketName } from '../proposal/market-name';
import { Indicator } from '../proposal/indicator'; import { Indicator } from '../proposal/indicator';
import { type ProposalNode } from '../proposal/proposal-utils';
const ProposalTypeTags = ({ const ProposalTypeTags = ({
proposal, proposal,
@ -540,10 +541,12 @@ const BatchProposalStateText = ({
export const ProposalHeader = ({ export const ProposalHeader = ({
proposal, proposal,
restData,
isListItem = true, isListItem = true,
voteState, voteState,
}: { }: {
proposal: Proposal | BatchProposal; proposal: Proposal | BatchProposal;
restData?: ProposalNode | null;
isListItem?: boolean; isListItem?: boolean;
voteState?: VoteState | null; voteState?: VoteState | null;
}) => { }) => {
@ -595,7 +598,7 @@ export const ProposalHeader = ({
)} )}
</div> </div>
<ProposalDetails proposal={proposal} /> <ProposalDetails proposal={proposal} />
<VoteBreakdown proposal={proposal} /> <VoteBreakdown proposal={proposal} restData={restData} />
</> </>
); );
}; };

View File

@ -91,6 +91,28 @@ export type ProposalNode = {
proposal: ProposalData; proposal: ProposalData;
proposalType: ProposalNodeType; proposalType: ProposalNodeType;
proposals: SubProposalData[]; proposals: SubProposalData[];
yes?: [
{
partyId: string;
elsPerMarket?: [
{
marketId: string;
els: string;
}
];
}
];
no?: [
{
partyId: string;
elsPerMarket?: [
{
marketId: string;
els: string;
}
];
}
];
}; };
type SingleProposalNode = ProposalNode & { type SingleProposalNode = ProposalNode & {

View File

@ -48,6 +48,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
<ProposalHeader <ProposalHeader
proposal={proposal} proposal={proposal}
restData={restData}
isListItem={false} isListItem={false}
voteState={voteState} voteState={voteState}
/> />

View File

@ -17,6 +17,7 @@ import {
import { useBatchVoteInformation } from '../../hooks/use-vote-information'; import { useBatchVoteInformation } from '../../hooks/use-vote-information';
import { MarketName } from '../proposal/market-name'; import { MarketName } from '../proposal/market-name';
import { Indicator } from '../proposal/indicator'; import { Indicator } from '../proposal/indicator';
import { type ProposalNode } from '../proposal/proposal-utils';
export const CompactVotes = ({ number }: { number: BigNumber }) => ( export const CompactVotes = ({ number }: { number: BigNumber }) => (
<CompactNumber <CompactNumber
@ -110,24 +111,64 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
export const VoteBreakdown = ({ export const VoteBreakdown = ({
proposal, proposal,
restData,
}: { }: {
proposal: Proposal | BatchProposal; proposal: Proposal | BatchProposal;
restData?: ProposalNode | null;
}) => { }) => {
if (proposal.__typename === 'Proposal') { if (proposal.__typename === 'Proposal') {
return <VoteBreakdownNormal proposal={proposal} />; return <VoteBreakdownNormal proposal={proposal} />;
} }
if (proposal.__typename === 'BatchProposal') { if (proposal.__typename === 'BatchProposal') {
return <VoteBreakdownBatch proposal={proposal} />; return <VoteBreakdownBatch proposal={proposal} restData={restData} />;
} }
return null; return null;
}; };
const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => { const VoteBreakdownBatch = ({
proposal,
restData,
}: {
proposal: BatchProposal;
restData?: ProposalNode | null;
}) => {
const [fullBreakdown, setFullBreakdown] = useState(false); const [fullBreakdown, setFullBreakdown] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const yesELS =
restData?.yes?.reduce((all, y) => {
if (y.elsPerMarket) {
y.elsPerMarket.forEach((item) => {
const share = Number(item.els);
if (all[item.marketId]) {
all[item.marketId].push(share);
} else {
all[item.marketId] = [share];
}
return all;
});
}
return all;
}, {} as Record<string, number[]>) || {};
const noELS =
restData?.no?.reduce((all, y) => {
if (y.elsPerMarket) {
y.elsPerMarket.forEach((item) => {
const share = Number(item.els);
if (all[item.marketId]) {
all[item.marketId].push(share);
} else {
all[item.marketId] = [share];
}
return all;
});
}
return all;
}, {} as Record<string, number[]>) || {};
const voteInfo = useBatchVoteInformation({ const voteInfo = useBatchVoteInformation({
terms: compact( terms: compact(
proposal.subProposals ? proposal.subProposals.map((p) => p?.terms) : [] proposal.subProposals ? proposal.subProposals.map((p) => p?.terms) : []
@ -194,6 +235,8 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
proposal={proposal} proposal={proposal}
votes={proposal.votes} votes={proposal.votes}
terms={p.terms} terms={p.terms}
yesELS={yesELS}
noELS={noELS}
/> />
); );
})} })}
@ -254,6 +297,8 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
proposal={proposal} proposal={proposal}
votes={proposal.votes} votes={proposal.votes}
terms={p.terms} terms={p.terms}
yesELS={yesELS}
noELS={noELS}
/> />
); );
})} })}
@ -271,17 +316,17 @@ const VoteBreakdownBatchSubProposal = ({
votes, votes,
terms, terms,
indicator, indicator,
yesELS,
noELS,
}: { }: {
proposal: BatchProposal; proposal: BatchProposal;
votes: VoteFieldsFragment; votes: VoteFieldsFragment;
terms: ProposalTermsFieldsFragment; terms: ProposalTermsFieldsFragment;
indicator?: number; indicator?: number;
yesELS: Record<string, number[]>;
noELS: Record<string, number[]>;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const voteInfo = useVoteInformation({
votes,
terms,
});
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN; const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket'; const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
@ -294,6 +339,15 @@ const VoteBreakdownBatchSubProposal = ({
marketId = terms.change.market.id; marketId = terms.change.market.id;
} }
const voteInfo = useVoteInformation({
votes,
terms,
// yes votes ELS for this specific proposal (market)
yesELS: marketId ? yesELS[marketId] : undefined,
// no votes ELS for this specific proposal (market)
noELS: marketId ? noELS[marketId] : undefined,
});
const marketName = marketId ? ( const marketName = marketId ? (
<> <>
: <MarketName marketId={marketId} /> : <MarketName marketId={marketId} />

View File

@ -8,13 +8,18 @@ import {
type VoteFieldsFragment, type VoteFieldsFragment,
} from '../__generated__/Proposals'; } from '../__generated__/Proposals';
import { type ProposalChangeType } from '../types'; import { type ProposalChangeType } from '../types';
import sum from 'lodash/sum';
export const useVoteInformation = ({ export const useVoteInformation = ({
votes, votes,
terms, terms,
yesELS,
noELS,
}: { }: {
votes: VoteFieldsFragment; votes: VoteFieldsFragment;
terms: ProposalTermsFieldsFragment; terms: ProposalTermsFieldsFragment;
yesELS?: number[];
noELS?: number[];
}) => { }) => {
const { const {
appState: { totalSupply, decimals }, appState: { totalSupply, decimals },
@ -31,7 +36,9 @@ export const useVoteInformation = ({
paramsForChange, paramsForChange,
votes, votes,
totalSupply, totalSupply,
decimals decimals,
yesELS,
noELS
); );
}; };
@ -72,7 +79,11 @@ const getVoteData = (
}, },
votes: ProposalFieldsFragment['votes'], votes: ProposalFieldsFragment['votes'],
totalSupply: BigNumber, totalSupply: BigNumber,
decimals: number decimals: number,
/** A list of ELS yes votes */
yesELS?: number[],
/** A list if ELS no votes */
noELS?: number[]
) => { ) => {
const requiredMajorityPercentage = params.requiredMajority const requiredMajorityPercentage = params.requiredMajority
? new BigNumber(params.requiredMajority).times(100) ? new BigNumber(params.requiredMajority).times(100)
@ -86,17 +97,31 @@ const getVoteData = (
addDecimal(votes.no.totalTokens ?? 0, decimals) addDecimal(votes.no.totalTokens ?? 0, decimals)
); );
const noEquityLikeShareWeight = !votes.no.totalEquityLikeShareWeight let noEquityLikeShareWeight = !votes.no.totalEquityLikeShareWeight
? new BigNumber(0) ? new BigNumber(0)
: new BigNumber(votes.no.totalEquityLikeShareWeight).times(100); : new BigNumber(votes.no.totalEquityLikeShareWeight).times(100);
// there's no meaningful `totalEquityLikeShareWeight` in batch proposals,
// it has to be deduced from `elsPerMarket` of `no` votes of given proposal
// data. (by REST DATA)
if (noELS != null) {
const noTotalELS = sum(noELS);
noEquityLikeShareWeight = new BigNumber(noTotalELS).times(100);
}
const yesTokens = new BigNumber( const yesTokens = new BigNumber(
addDecimal(votes.yes.totalTokens ?? 0, decimals) addDecimal(votes.yes.totalTokens ?? 0, decimals)
); );
const yesEquityLikeShareWeight = !votes.yes.totalEquityLikeShareWeight let yesEquityLikeShareWeight = !votes.yes.totalEquityLikeShareWeight
? new BigNumber(0) ? new BigNumber(0)
: new BigNumber(votes.yes.totalEquityLikeShareWeight).times(100); : new BigNumber(votes.yes.totalEquityLikeShareWeight).times(100);
// there's no meaningful `totalEquityLikeShareWeight` in batch proposals,
// it has to be deduced from `elsPerMarket` of `yes` votes of given proposal
// data. (by REST DATA)
if (noELS != null) {
const yesTotalELS = sum(yesELS);
yesEquityLikeShareWeight = new BigNumber(yesTotalELS).times(100);
}
const totalTokensVoted = yesTokens.plus(noTokens); const totalTokensVoted = yesTokens.plus(noTokens);

View File

@ -30,8 +30,6 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
setStudies, setStudies,
setStudySizes, setStudySizes,
setOverlays, setOverlays,
state,
setState,
} = useChartSettings(); } = useChartSettings();
const pennantChart = ( const pennantChart = (
@ -68,10 +66,6 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
onIntervalChange={(newInterval) => { onIntervalChange={(newInterval) => {
setInterval(fromTradingViewResolution(newInterval)); setInterval(fromTradingViewResolution(newInterval));
}} }}
onAutoSaveNeeded={(data) => {
setState(data);
}}
state={state}
/> />
); );
} }

View File

@ -9,7 +9,6 @@ type StudySizes = { [S in Study]?: number };
export type Chartlib = 'pennant' | 'tradingview'; export type Chartlib = 'pennant' | 'tradingview';
interface StoredSettings { interface StoredSettings {
state: object | undefined; // Don't see a better type provided from TradingView type definitions
chartlib: Chartlib; chartlib: Chartlib;
// For interval we use the enum from @vegaprotocol/types, this is to make mapping between different // For interval we use the enum from @vegaprotocol/types, this is to make mapping between different
// chart types easier and more consistent // chart types easier and more consistent
@ -30,7 +29,6 @@ const STUDY_ORDER: Study[] = [
]; ];
export const DEFAULT_CHART_SETTINGS = { export const DEFAULT_CHART_SETTINGS = {
state: undefined,
chartlib: 'pennant' as const, chartlib: 'pennant' as const,
interval: Interval.INTERVAL_I15M, interval: Interval.INTERVAL_I15M,
type: ChartType.CANDLE, type: ChartType.CANDLE,
@ -47,7 +45,6 @@ export const useChartSettingsStore = create<
setStudies: (studies?: Study[]) => void; setStudies: (studies?: Study[]) => void;
setStudySizes: (sizes: number[]) => void; setStudySizes: (sizes: number[]) => void;
setChartlib: (lib: Chartlib) => void; setChartlib: (lib: Chartlib) => void;
setState: (state: object) => void;
} }
>()( >()(
persist( persist(
@ -95,9 +92,6 @@ export const useChartSettingsStore = create<
state.chartlib = lib; state.chartlib = lib;
}); });
}, },
setState: (state) => {
set({ state });
},
})), })),
{ {
name: 'vega_candles_chart_store', name: 'vega_candles_chart_store',
@ -151,7 +145,5 @@ export const useChartSettings = () => {
setOverlays: settings.setOverlays, setOverlays: settings.setOverlays,
setStudySizes: settings.setStudySizes, setStudySizes: settings.setStudySizes,
setChartlib: settings.setChartlib, setChartlib: settings.setChartlib,
state: settings.state,
setState: settings.setState,
}; };
}; };

View File

@ -62,3 +62,10 @@ export const DENY_LIST: Record<string, string[]> = {
'fdf0ec118d98393a7702cf72e46fc87ad680b152f64b2aac59e093ac2d688fbb', 'fdf0ec118d98393a7702cf72e46fc87ad680b152f64b2aac59e093ac2d688fbb',
], ],
}; };
// We need a record of USDT on mainnet as it needs special handling for
// deposits and approvals due to the contract not conforming exactly
// to ERC20
export const USDT_ID = {
MAINNET: 'bf1e88d19db4b3ca0d1d5bdb73718a01686b18cf731ca26adedf3c8b83802bba',
} as const;

View File

@ -1,5 +1,9 @@
import type { Asset } from '@vegaprotocol/assets'; import { USDT_ID, type Asset } from '@vegaprotocol/assets';
import { EtherscanLink } from '@vegaprotocol/environment'; import {
EtherscanLink,
Networks,
useEnvironment,
} from '@vegaprotocol/environment';
import { Intent, Notification } from '@vegaprotocol/ui-toolkit'; import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
import { import {
formatNumber, formatNumber,
@ -15,7 +19,7 @@ import { useT } from './use-t';
interface ApproveNotificationProps { interface ApproveNotificationProps {
isActive: boolean; isActive: boolean;
selectedAsset?: Asset; selectedAsset?: Asset;
onApprove: () => void; onApprove: (amount?: string) => void;
approved: boolean; approved: boolean;
balances: DepositBalances | null; balances: DepositBalances | null;
amount: string; amount: string;
@ -33,6 +37,7 @@ export const ApproveNotification = ({
approveTxId, approveTxId,
intent = Intent.Warning, intent = Intent.Warning,
}: ApproveNotificationProps) => { }: ApproveNotificationProps) => {
const { VEGA_ENV } = useEnvironment();
const t = useT(); const t = useT();
const tx = useEthTransactionStore((state) => { const tx = useEthTransactionStore((state) => {
return state.transactions.find((t) => t?.id === approveTxId); return state.transactions.find((t) => t?.id === approveTxId);
@ -64,28 +69,45 @@ export const ApproveNotification = ({
text: t('Approve {{assetSymbol}}', { text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol, assetSymbol: selectedAsset?.symbol,
}), }),
action: onApprove, action: () => onApprove(),
dataTestId: 'approve-submit', dataTestId: 'approve-submit',
}} }}
/> />
</div> </div>
); );
let message = t('Approve again to deposit more than {{allowance}}', {
allowance: formatNumber(balances.allowance.toString()),
});
const buttonProps = {
size: 'small' as const,
text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol,
}),
action: () => onApprove(),
dataTestId: 'reapprove-submit',
};
if (VEGA_ENV === Networks.MAINNET && selectedAsset.id === USDT_ID[VEGA_ENV]) {
message = t(
'USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.',
{
allowance: formatNumber(balances.allowance.toString()),
}
);
buttonProps.text = t('Revoke {{assetSymbol}} approval', {
assetSymbol: selectedAsset?.symbol,
});
buttonProps.action = () => onApprove('0');
}
const reApprovePrompt = ( const reApprovePrompt = (
<div className="mb-4"> <div className="mb-4">
<Notification <Notification
intent={intent} intent={intent}
testId="reapprove-default" testId="reapprove-default"
message={t('Approve again to deposit more than {{allowance}}', { message={message}
allowance: formatNumber(balances.allowance.toString()), buttonProps={buttonProps}
})}
buttonProps={{
size: 'small',
text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol,
}),
action: onApprove,
dataTestId: 'reapprove-submit',
}}
/> />
</div> </div>
); );

View File

@ -58,7 +58,7 @@ export interface DepositFormProps {
onSelectAsset: (assetId: string) => void; onSelectAsset: (assetId: string) => void;
handleAmountChange: (amount: string) => void; handleAmountChange: (amount: string) => void;
onDisconnect: () => void; onDisconnect: () => void;
submitApprove: () => void; submitApprove: (amount?: string) => void;
approveTxId: number | null; approveTxId: number | null;
submitFaucet: () => void; submitFaucet: () => void;
faucetTxId: number | null; faucetTxId: number | null;
@ -423,8 +423,8 @@ export const DepositForm = ({
isActive={isActive} isActive={isActive}
approveTxId={approveTxId} approveTxId={approveTxId}
selectedAsset={selectedAsset} selectedAsset={selectedAsset}
onApprove={() => { onApprove={(amount) => {
submitApprove(); submitApprove(amount);
setApproveNotificationIntent(Intent.Warning); setApproveNotificationIntent(Intent.Warning);
}} }}
balances={balances} balances={balances}

View File

@ -35,11 +35,11 @@ export const useSubmitApproval = (
reset: () => { reset: () => {
setId(null); setId(null);
}, },
perform: () => { perform: (amount?: string) => {
if (!asset || !config) return; if (!asset || !config) return;
const id = createEthTransaction(contract, 'approve', [ const id = createEthTransaction(contract, 'approve', [
config?.collateral_bridge_contract.address, config?.collateral_bridge_contract.address,
MaxUint256.toString(), amount ? amount : MaxUint256.toString(),
]); ]);
setId(id); setId(id);
}, },

View File

@ -4,6 +4,7 @@
"Approval failed": "Approval failed", "Approval failed": "Approval failed",
"Approve {{assetSymbol}}": "Approve {{assetSymbol}}", "Approve {{assetSymbol}}": "Approve {{assetSymbol}}",
"Approve again to deposit more than {{allowance}}": "Approve again to deposit more than {{allowance}}", "Approve again to deposit more than {{allowance}}": "Approve again to deposit more than {{allowance}}",
"USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.": "USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.",
"Asset": "Asset", "Asset": "Asset",
"Balance available": "Balance available", "Balance available": "Balance available",
"Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet": "Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet", "Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet": "Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet",
@ -23,6 +24,7 @@
"Please select": "Please select", "Please select": "Please select",
"Please select an asset": "Please select an asset", "Please select an asset": "Please select an asset",
"Remaining deposit allowance": "Remaining deposit allowance", "Remaining deposit allowance": "Remaining deposit allowance",
"Revoke {{assetSymbol}} approval": "Revoke {{assetSymbol}} approval",
"Select from wallet": "Select from wallet", "Select from wallet": "Select from wallet",
"The {{symbol}} faucet is not available at this time": "The {{symbol}} faucet is not available at this time", "The {{symbol}} faucet is not available at this time": "The {{symbol}} faucet is not available at this time",
"The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.": "The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.", "The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.": "The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.",

View File

@ -1,7 +1,7 @@
import { useScript } from '@vegaprotocol/react-helpers'; import { useScript } from '@vegaprotocol/react-helpers';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { useT } from './use-t'; import { useT } from './use-t';
import { TradingView, type OnAutoSaveNeededCallback } from './trading-view'; import { TradingView } from './trading-view';
import { CHARTING_LIBRARY_FILE, type ResolutionString } from './constants'; import { CHARTING_LIBRARY_FILE, type ResolutionString } from './constants';
export const TradingViewContainer = ({ export const TradingViewContainer = ({
@ -10,16 +10,12 @@ export const TradingViewContainer = ({
marketId, marketId,
interval, interval,
onIntervalChange, onIntervalChange,
onAutoSaveNeeded,
state,
}: { }: {
libraryPath: string; libraryPath: string;
libraryHash: string; libraryHash: string;
marketId: string; marketId: string;
interval: ResolutionString; interval: ResolutionString;
onIntervalChange: (interval: string) => void; onIntervalChange: (interval: string) => void;
onAutoSaveNeeded: OnAutoSaveNeededCallback;
state: object | undefined;
}) => { }) => {
const t = useT(); const t = useT();
const scriptState = useScript( const scriptState = useScript(
@ -49,8 +45,6 @@ export const TradingViewContainer = ({
marketId={marketId} marketId={marketId}
interval={interval} interval={interval}
onIntervalChange={onIntervalChange} onIntervalChange={onIntervalChange}
onAutoSaveNeeded={onAutoSaveNeeded}
state={state}
/> />
); );
}; };

View File

@ -25,15 +25,11 @@ export const TradingView = ({
libraryPath, libraryPath,
interval, interval,
onIntervalChange, onIntervalChange,
onAutoSaveNeeded,
state,
}: { }: {
marketId: string; marketId: string;
libraryPath: string; libraryPath: string;
interval: ResolutionString; interval: ResolutionString;
onIntervalChange: (interval: string) => void; onIntervalChange: (interval: string) => void;
onAutoSaveNeeded: OnAutoSaveNeededCallback;
state: object | undefined;
}) => { }) => {
const { isMobile } = useScreenDimensions(); const { isMobile } = useScreenDimensions();
const { theme } = useThemeSwitcher(); const { theme } = useThemeSwitcher();
@ -108,7 +104,6 @@ export const TradingView = ({
backgroundColor: overrides['paneProperties.background'], backgroundColor: overrides['paneProperties.background'],
}, },
auto_save_delay: 1, auto_save_delay: 1,
saved_data: state,
}; };
widgetRef.current = new window.TradingView.widget(widgetOptions); widgetRef.current = new window.TradingView.widget(widgetOptions);
@ -117,25 +112,12 @@ export const TradingView = ({
if (!widgetRef.current) return; if (!widgetRef.current) return;
const activeChart = widgetRef.current.activeChart(); const activeChart = widgetRef.current.activeChart();
activeChart.createStudy('Volume');
if (!state) {
// If chart has loaded with no state, create a volume study
activeChart.createStudy('Volume');
}
// Subscribe to interval changes so it can be persisted in chart settings // Subscribe to interval changes so it can be persisted in chart settings
activeChart.onIntervalChanged().subscribe(null, onIntervalChange); activeChart.onIntervalChanged().subscribe(null, onIntervalChange);
}); });
widgetRef.current.subscribe('onAutoSaveNeeded', () => {
if (!widgetRef.current) return;
widgetRef.current.save((newState) => {
onAutoSaveNeeded(newState);
});
});
}, [ }, [
state,
datafeed, datafeed,
interval, interval,
prevTheme, prevTheme,
@ -145,7 +127,6 @@ export const TradingView = ({
language, language,
libraryPath, libraryPath,
isMobile, isMobile,
onAutoSaveNeeded,
onIntervalChange, onIntervalChange,
]); ]);

View File

@ -27,6 +27,7 @@ export const useEthTransactionManager = () => {
confirmations: 0, confirmations: 0,
notify: true, notify: true,
}); });
const { const {
contract, contract,
methodName, methodName,
@ -44,6 +45,7 @@ export const useEthTransactionManager = () => {
) { ) {
throw new Error('method not found on contract'); throw new Error('method not found on contract');
} }
await contract.contract.callStatic[methodName](...args); await contract.contract.callStatic[methodName](...args);
} catch (err) { } catch (err) {
update(transaction.id, { update(transaction.id, {