Skip to content

Commit 15f6786

Browse files
authored
Implement set error flag (#121)
* Implement set error flag
1 parent 4c4a667 commit 15f6786

File tree

11 files changed

+146
-123
lines changed

11 files changed

+146
-123
lines changed

components/ProposalActions.tsx

Lines changed: 128 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { useEffect, useState } from 'react';
22
import { useHasVoteTimeExpired } from '../hooks/useHasVoteTimeExpired';
33
import useRealm from '../hooks/useRealm';
4-
import { getSignatoryRecordAddress } from '@solana/spl-governance';
4+
import {
5+
getSignatoryRecordAddress,
6+
InstructionExecutionStatus,
7+
} from '@solana/spl-governance';
58
import useWalletStore, {
69
EnhancedProposalState,
710
} from '../stores/useWalletStore';
@@ -15,11 +18,14 @@ import { Proposal } from '@solana/spl-governance';
1518
import { ProgramAccount } from '@solana/spl-governance';
1619
import { cancelProposal } from 'actions/cancelProposal';
1720
import { getProgramVersionForRealm } from '@models/registry/api';
21+
import { flagInstructionError } from 'actions/flagInstructionError';
22+
import useProposal from '@hooks/useProposal';
1823

1924
const ProposalActionsPanel = () => {
2025
const { governance, proposal, proposalOwner } = useWalletStore(
2126
(s) => s.selectedProposal,
2227
);
28+
const { instructions } = useProposal();
2329
const { realmInfo } = useRealm();
2430
const wallet = useWalletStore((s) => s.current);
2531
const connected = useWalletStore((s) => s.connected);
@@ -69,6 +75,20 @@ const ProposalActionsPanel = () => {
6975
wallet.publicKey,
7076
);
7177

78+
const canSetFlagToExecutionError =
79+
proposal &&
80+
governance &&
81+
proposalOwner &&
82+
wallet?.publicKey &&
83+
proposalOwner?.account.governingTokenOwner.equals(wallet.publicKey) &&
84+
(proposal?.account.state === EnhancedProposalState.Succeeded ||
85+
proposal?.account.state === EnhancedProposalState.Executing ||
86+
proposal?.account.state === EnhancedProposalState.ExecutingWithErrors) &&
87+
Object.values(instructions).some(
88+
(instruction) =>
89+
instruction.account.executionStatus === InstructionExecutionStatus.None,
90+
);
91+
7292
const signOffTooltipContent = !connected
7393
? 'Connect your wallet to sign off this proposal'
7494
: !signatoryRecord
@@ -102,6 +122,11 @@ const ProposalActionsPanel = () => {
102122
!hasVoteTimeExpired
103123
? 'Proposal is being voting right now, you need to wait the vote to finish to be able to finalize it.'
104124
: '';
125+
126+
const setFlagToExecutionErrorTooltipContent = !connected
127+
? 'Connect your wallet to set the error flag for this proposal.'
128+
: 'Set error execution flag for this proposal.';
129+
105130
const handleFinalizeVote = async () => {
106131
try {
107132
if (proposal && realmInfo && governance) {
@@ -127,6 +152,51 @@ const ProposalActionsPanel = () => {
127152
}
128153
};
129154

155+
const handleSetExecutionErrorFlag = async () => {
156+
try {
157+
if (proposal && realmInfo && governance) {
158+
const rpcContext = new RpcContext(
159+
proposal.owner,
160+
getProgramVersionForRealm(realmInfo),
161+
wallet!,
162+
connection.current,
163+
connection.endpoint,
164+
);
165+
166+
if (Object.keys(instructions).length === 0) {
167+
notify({
168+
type: 'info',
169+
message: 'Cannot set the error flag',
170+
description: 'The proposal does not contain any instruction',
171+
});
172+
173+
return;
174+
}
175+
176+
const filteredInstructions = Object.values(instructions).filter(
177+
(instruction) =>
178+
instruction.account.executionStatus ===
179+
InstructionExecutionStatus.None,
180+
);
181+
182+
// Set flag error for one instruction after another until there are no more to set flag to
183+
for (const instruction of filteredInstructions) {
184+
await flagInstructionError(rpcContext, proposal, instruction.pubkey);
185+
}
186+
187+
await fetchRealm(realmInfo!.programId, realmInfo!.realmId);
188+
}
189+
} catch (error) {
190+
notify({
191+
type: 'error',
192+
message: `Error: Could not finalize vote.`,
193+
description: `${error}`,
194+
});
195+
196+
console.error('error finalizing vote', error);
197+
}
198+
};
199+
130200
const handleSignOffProposal = async () => {
131201
try {
132202
if (proposal && realmInfo) {
@@ -157,6 +227,7 @@ const ProposalActionsPanel = () => {
157227
console.error('error sign off', error);
158228
}
159229
};
230+
160231
const handleCancelProposal = async (
161232
proposal: ProgramAccount<Proposal> | undefined,
162233
) => {
@@ -184,51 +255,63 @@ const ProposalActionsPanel = () => {
184255
console.error('error cancelling proposal', error);
185256
}
186257
};
258+
259+
if (
260+
!canCancelProposal &&
261+
!canSignOff &&
262+
!canFinalizeVote &&
263+
!canSetFlagToExecutionError
264+
) {
265+
return null;
266+
}
267+
187268
return (
188-
<>
189-
{EnhancedProposalState.Cancelled === proposal?.account.state ||
190-
EnhancedProposalState.Succeeded === proposal?.account.state ||
191-
EnhancedProposalState.Outdated === proposal?.account.state ||
192-
EnhancedProposalState.Defeated === proposal?.account.state ||
193-
(!canCancelProposal && !canSignOff && !canFinalizeVote) ? null : (
194-
<div>
195-
<div className="bg-bkg-2 rounded-lg p-6 space-y-6 flex justify-center items-center text-center flex-col w-full mt-4">
196-
{canSignOff && (
197-
<Button
198-
tooltipMessage={signOffTooltipContent}
199-
className="w-1/2"
200-
onClick={handleSignOffProposal}
201-
disabled={!connected || !canSignOff}
202-
>
203-
Sign Off
204-
</Button>
205-
)}
206-
207-
{canCancelProposal && (
208-
<SecondaryButton
209-
tooltipMessage={cancelTooltipContent}
210-
className="w-1/2"
211-
onClick={() => handleCancelProposal(proposal)}
212-
disabled={!connected}
213-
>
214-
Cancel
215-
</SecondaryButton>
216-
)}
217-
218-
{canFinalizeVote && (
219-
<Button
220-
tooltipMessage={finalizeVoteTooltipContent}
221-
className="w-1/2"
222-
onClick={handleFinalizeVote}
223-
disabled={!connected || !canFinalizeVote}
224-
>
225-
Finalize
226-
</Button>
227-
)}
228-
</div>
229-
</div>
230-
)}
231-
</>
269+
<div>
270+
<div className="bg-bkg-2 rounded-lg p-6 space-y-6 flex justify-center items-center text-center flex-col w-full mt-4">
271+
{canSignOff && (
272+
<Button
273+
tooltipMessage={signOffTooltipContent}
274+
className="w-1/2"
275+
onClick={handleSignOffProposal}
276+
disabled={!connected || !canSignOff}
277+
>
278+
Sign Off
279+
</Button>
280+
)}
281+
282+
{canCancelProposal && (
283+
<SecondaryButton
284+
tooltipMessage={cancelTooltipContent}
285+
className="w-1/2"
286+
onClick={() => handleCancelProposal(proposal)}
287+
disabled={!connected}
288+
>
289+
Cancel
290+
</SecondaryButton>
291+
)}
292+
293+
{canFinalizeVote && (
294+
<Button
295+
tooltipMessage={finalizeVoteTooltipContent}
296+
className="w-1/2"
297+
onClick={handleFinalizeVote}
298+
disabled={!connected || !canFinalizeVote}
299+
>
300+
Finalize
301+
</Button>
302+
)}
303+
304+
{canSetFlagToExecutionError && (
305+
<Button
306+
tooltipMessage={setFlagToExecutionErrorTooltipContent}
307+
onClick={handleSetExecutionErrorFlag}
308+
disabled={!connected}
309+
>
310+
Set Execution Error Flag
311+
</Button>
312+
)}
313+
</div>
314+
</div>
232315
);
233316
};
234317

components/ProposalFilter.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const initialFilterSettings: Filters = {
1919
[EnhancedProposalState.Cancelled]: false,
2020
[EnhancedProposalState.Defeated]: true,
2121
[EnhancedProposalState.ExecutingWithErrors]: true,
22-
[EnhancedProposalState.Outdated]: false,
2322
};
2423

2524
const StyledAlertCount = styled.span`
@@ -159,15 +158,6 @@ const ProposalFilter = ({ filters, setFilters }) => {
159158
}
160159
/>
161160
</div>
162-
<div className="flex items-center justify-between pb-2">
163-
Outdated
164-
<Switch
165-
checked={filterSettings[EnhancedProposalState.Outdated]}
166-
onChange={(checked) =>
167-
handleFilters(EnhancedProposalState.Outdated, checked)
168-
}
169-
/>
170-
</div>
171161
<div className="flex items-center justify-between">
172162
Voting
173163
<Switch

components/ProposalStatusBadge.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ function getProposalStateLabel(
1313
switch (state) {
1414
case EnhancedProposalState.ExecutingWithErrors:
1515
return 'Execution Errors';
16-
case EnhancedProposalState.Outdated:
17-
return 'Outdated';
1816
case EnhancedProposalState.Voting:
1917
// If there is no tipping point and voting period ends then proposal stays in Voting state and needs to be manually finalized
2018
return hasVoteEnded ? 'Finalizing' : 'Voting';
@@ -47,10 +45,6 @@ function getProposalStateStyle(state: EnhancedProposalState) {
4745
return 'border border-red text-red';
4846
}
4947

50-
if (state === EnhancedProposalState.Outdated) {
51-
return 'border border-orange text-orange';
52-
}
53-
5448
return 'border border-fgd-3 text-fgd-3';
5549
}
5650

components/instructions/ExecuteAllInstructionButton.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ export function ExecuteAllInstructionButton({
9595
EnhancedProposalState.Executing,
9696
EnhancedProposalState.ExecutingWithErrors,
9797
EnhancedProposalState.Succeeded,
98-
EnhancedProposalState.Outdated,
9998
].includes(proposal.account.state)
10099
) {
101100
return null;

components/instructions/ExecuteInstructionButton.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,7 @@ export function ExecuteInstructionButton({
127127
if (
128128
proposal.account.state !== EnhancedProposalState.Executing &&
129129
proposal.account.state !== EnhancedProposalState.ExecutingWithErrors &&
130-
proposal.account.state !== EnhancedProposalState.Succeeded &&
131-
proposal.account.state !== EnhancedProposalState.Outdated
130+
proposal.account.state !== EnhancedProposalState.Succeeded
132131
) {
133132
return null;
134133
}

components/instructions/FlagInstructionErrorButton.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { flagInstructionError } from 'actions/flagInstructionError';
33
import {
44
InstructionExecutionStatus,
55
Proposal,
6+
ProposalState,
67
ProposalTransaction,
78
TokenOwnerRecord,
89
} from '@solana/spl-governance';
@@ -34,6 +35,7 @@ export function FlagInstructionErrorButton({
3435
const connection = useWalletStore((s) => s.connection);
3536

3637
if (
38+
proposal.account.state === ProposalState.ExecutingWithErrors ||
3739
playState !== PlayState.Error ||
3840
proposalInstruction.account.executionStatus !==
3941
InstructionExecutionStatus.Error ||

components/instructions/instructionCard.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ExternalLinkIcon } from '@heroicons/react/outline';
33
import {
44
AccountMetaData,
55
Proposal,
6+
ProposalState,
67
ProposalTransaction,
78
} from '@solana/spl-governance';
89
import {
@@ -169,10 +170,12 @@ export default function InstructionCard({
169170
></img>
170171
)}
171172
</h3>
173+
172174
<InstructionProgram
173175
endpoint={connection.endpoint}
174176
programId={proposalInstruction.account.getSingleInstruction().programId}
175-
></InstructionProgram>
177+
/>
178+
176179
<div className="border-b border-bkg-4 mb-6">
177180
{proposalInstruction.account
178181
.getSingleInstruction()
@@ -202,7 +205,7 @@ export default function InstructionCard({
202205
<img src={nftImgUrl}></img>
203206
</div>
204207
) : (
205-
<InstructionData descriptor={descriptor}></InstructionData>
208+
<InstructionData descriptor={descriptor} />
206209
)}
207210

208211
{
@@ -236,7 +239,8 @@ export default function InstructionCard({
236239
proposalInstruction={proposalInstruction}
237240
/>
238241

239-
{proposal && (
242+
{proposal &&
243+
proposal.account.state !== ProposalState.ExecutingWithErrors ? (
240244
<ExecuteInstructionButton
241245
disabled={
242246
isInstructionAboutOrcaWhirlpoolOpenPosition && !additionalSigner
@@ -247,7 +251,7 @@ export default function InstructionCard({
247251
setPlaying={setPlaying}
248252
additionalSigner={additionalSigner ?? undefined}
249253
/>
250-
)}
254+
) : null}
251255
</div>
252256
</div>
253257
);

components/instructions/instructionPanel.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Disclosure } from '@headlessui/react';
44
import { ChevronDownIcon } from '@heroicons/react/solid';
55
import { useEffect, useRef, useState } from 'react';
66
import useWalletStore from 'stores/useWalletStore';
7-
import { RpcContext } from '@solana/spl-governance';
7+
import { ProposalState, RpcContext } from '@solana/spl-governance';
88
import useRealm from '@hooks/useRealm';
99
import { getProgramVersionForRealm } from '@models/registry/api';
1010
import {
@@ -104,7 +104,9 @@ export function InstructionPanel() {
104104
</div>
105105
))}
106106

107-
{proposal && proposalInstructions.length > 1 && (
107+
{proposal &&
108+
proposalInstructions.length > 1 &&
109+
proposal.account.state !== ProposalState.ExecutingWithErrors ? (
108110
<div className="flex justify-end">
109111
<ExecuteAllInstructionButton
110112
proposal={proposal}
@@ -113,7 +115,7 @@ export function InstructionPanel() {
113115
setPlaying={setPlaying}
114116
/>
115117
</div>
116-
)}
118+
) : null}
117119
</Disclosure.Panel>
118120
</>
119121
)}

pages/dao/[symbol]/proposal/[pk].tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ const Proposal = () => {
4040
(proposal.account.state === EnhancedProposalState.Completed ||
4141
proposal.account.state === EnhancedProposalState.Executing ||
4242
proposal.account.state === EnhancedProposalState.SigningOff ||
43-
proposal.account.state === EnhancedProposalState.Succeeded ||
44-
proposal.account.state === EnhancedProposalState.Outdated);
43+
proposal.account.state === EnhancedProposalState.Succeeded);
4544

4645
useEffect(() => {
4746
const handleResolveDescription = async () => {

0 commit comments

Comments
 (0)