11import { useEffect , useState } from 'react' ;
22import { useHasVoteTimeExpired } from '../hooks/useHasVoteTimeExpired' ;
33import useRealm from '../hooks/useRealm' ;
4- import { getSignatoryRecordAddress } from '@solana/spl-governance' ;
4+ import {
5+ getSignatoryRecordAddress ,
6+ InstructionExecutionStatus ,
7+ } from '@solana/spl-governance' ;
58import useWalletStore , {
69 EnhancedProposalState ,
710} from '../stores/useWalletStore' ;
@@ -15,11 +18,14 @@ import { Proposal } from '@solana/spl-governance';
1518import { ProgramAccount } from '@solana/spl-governance' ;
1619import { cancelProposal } from 'actions/cancelProposal' ;
1720import { getProgramVersionForRealm } from '@models/registry/api' ;
21+ import { flagInstructionError } from 'actions/flagInstructionError' ;
22+ import useProposal from '@hooks/useProposal' ;
1823
1924const 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
0 commit comments