|
| 1 | +const types = require('ethernaut-common/src/validation/types') |
| 2 | +const output = require('ethernaut-common/src/ui/output') |
| 3 | +const Ballots = require('../internal/agora/Ballots') |
| 4 | +const agora = require('../internal/agora/agoraInstance') |
| 5 | + |
| 6 | +require('../scopes/optigov') |
| 7 | + .task('ballots', 'Interacts with ballots on Agora: retrieve or submit') |
| 8 | + .addParam( |
| 9 | + 'action', |
| 10 | + 'Action to perform: "get" or "submit"', |
| 11 | + undefined, |
| 12 | + types.string, |
| 13 | + ) |
| 14 | + .addParam('roundId', 'RetroFunding round ID', undefined, types.string) |
| 15 | + .addParam( |
| 16 | + 'ballotContent', |
| 17 | + 'Ballot content as a JSON string (optional for submit)', |
| 18 | + undefined, |
| 19 | + types.string, |
| 20 | + ) |
| 21 | + |
| 22 | + .setAction(async ({ action, roundId, ballotContent }, hre) => { |
| 23 | + try { |
| 24 | + const ballots = new Ballots(agora) |
| 25 | + |
| 26 | + if (action === 'get') { |
| 27 | + const signers = await hre.ethers.getSigners() |
| 28 | + if (!signers.length) { |
| 29 | + throw new Error('No signers available.') |
| 30 | + } |
| 31 | + const signer = signers[0] |
| 32 | + const ballot = await ballots.getBallot(roundId, signer.address) |
| 33 | + // Format the ballot output if it's an object |
| 34 | + const ballotResult = |
| 35 | + typeof ballot === 'string' ? ballot : formatBallot(ballot) |
| 36 | + return output.resultBox( |
| 37 | + ballotResult, |
| 38 | + `Ballot for Round ${roundId} & ${signer.address}`, |
| 39 | + ) |
| 40 | + } else if (action === 'submit') { |
| 41 | + const signers = await hre.ethers.getSigners() |
| 42 | + if (!signers.length) { |
| 43 | + throw new Error('No signers available.') |
| 44 | + } |
| 45 | + const signer = signers[0] |
| 46 | + // Prepare content to be signed |
| 47 | + const defaultContent = { |
| 48 | + allocations: [{}], |
| 49 | + os_only: true, |
| 50 | + os_multiplier: 0, |
| 51 | + } |
| 52 | + const contentToProcess = ballotContent |
| 53 | + ? ballotContent |
| 54 | + : JSON.stringify(defaultContent) |
| 55 | + const parsedContent = ballotContent |
| 56 | + ? JSON.parse(ballotContent) |
| 57 | + : defaultContent |
| 58 | + const computedSignature = await signer.signMessage(contentToProcess) |
| 59 | + |
| 60 | + const payload = { |
| 61 | + address: signer.address, |
| 62 | + ballot_content: parsedContent, |
| 63 | + signature: computedSignature, |
| 64 | + } |
| 65 | + const result = await ballots.submitBallot( |
| 66 | + roundId, |
| 67 | + signer.address, |
| 68 | + payload, |
| 69 | + ) |
| 70 | + const resultString = |
| 71 | + typeof result === 'string' ? result : JSON.stringify(result) |
| 72 | + return output.resultBox( |
| 73 | + resultString, |
| 74 | + `Submit Ballot for Round ${roundId} & ${signer.address}`, |
| 75 | + ) |
| 76 | + } else { |
| 77 | + throw new Error('Invalid action. Use "get" or "submit".') |
| 78 | + } |
| 79 | + } catch (err) { |
| 80 | + return output.errorBox(err) |
| 81 | + } |
| 82 | + }) |
| 83 | + |
| 84 | +// Utility: formats the ballot object for display |
| 85 | +function formatBallot(ballot) { |
| 86 | + let formatted = '' |
| 87 | + formatted += `Address: ${ballot.address}\n` |
| 88 | + formatted += `Round ID: ${ballot.round_id}\n` |
| 89 | + formatted += `Status: ${ballot.status}\n` |
| 90 | + formatted += `Budget: ${ballot.budget}\n` |
| 91 | + formatted += `Created At: ${ballot.created_at}\n` |
| 92 | + formatted += `Updated At: ${ballot.updated_at}\n` |
| 93 | + formatted += `Published At: ${ballot.published_at}\n\n` |
| 94 | + |
| 95 | + if (Array.isArray(ballot.category_allocations)) { |
| 96 | + formatted += 'Category Allocations:\n' |
| 97 | + ballot.category_allocations.forEach((cat) => { |
| 98 | + formatted += ` - Category Slug: ${cat.category_slug}, Allocation: ${cat.allocation}, Locked: ${cat.locked}\n` |
| 99 | + }) |
| 100 | + formatted += '\n' |
| 101 | + } |
| 102 | + |
| 103 | + if (Array.isArray(ballot.projects_allocations)) { |
| 104 | + formatted += 'Projects Allocations:\n' |
| 105 | + ballot.projects_allocations.forEach((proj) => { |
| 106 | + formatted += ` - Project ID: ${proj.project_id}, Name: ${proj.name}, Image: ${proj.image}, Position: ${proj.position}, Allocation: ${proj.allocation}, Impact: ${proj.impact}\n` |
| 107 | + }) |
| 108 | + formatted += '\n' |
| 109 | + } |
| 110 | + |
| 111 | + if (Array.isArray(ballot.projects_to_be_evaluated)) { |
| 112 | + formatted += 'Projects to be Evaluated:\n' |
| 113 | + ballot.projects_to_be_evaluated.forEach((project) => { |
| 114 | + formatted += ` - ${project}\n` |
| 115 | + }) |
| 116 | + formatted += '\n' |
| 117 | + } |
| 118 | + |
| 119 | + if (ballot.payload_for_signature) { |
| 120 | + formatted += 'Payload for Signature:\n' |
| 121 | + formatted += ` Budget: ${ballot.payload_for_signature.budget}\n` |
| 122 | + formatted += ` Category Allocation: ${JSON.stringify(ballot.payload_for_signature.category_allocation, null, 2)}\n` |
| 123 | + formatted += ` Projects Allocation: ${JSON.stringify(ballot.payload_for_signature.projects_allocation, null, 2)}\n` |
| 124 | + } |
| 125 | + |
| 126 | + return formatted |
| 127 | +} |
0 commit comments