Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions packages/ethernaut-optigov/src/internal/agora/Ballots.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const debug = require('ethernaut-common/src/ui/debug')

class Ballots {
constructor(agora) {
this.agora = agora
}

// Retrieve a specific ballot for a RetroFunding round
async getBallot(roundId, addressOrEnsName) {
try {
const axiosInstance = this.agora.createAxiosInstance()
const response = await axiosInstance.get(
`/retrofunding/rounds/${roundId}/ballots/${addressOrEnsName}`,
)
// Convert response.data to a string if it's not one
const dataString =
typeof response.data === 'string'
? response.data
: JSON.stringify(response.data)
debug.log(`Ballot: ${dataString}`, 'ethernaut-optigov')
return response.data
} catch (error) {
this.agora.handleError(error)
}
}

// Submit the ballot content as final for the round
async submitBallot(roundId, addressOrEnsName, payload) {
try {
const axiosInstance = this.agora.createAxiosInstance()
const response = await axiosInstance.post(
`/retrofunding/rounds/${roundId}/ballots/${addressOrEnsName}/submit`,
payload,
)
debug.log(`Submit Ballot Response: ${response.data}`, 'ethernaut-optigov')
return response.data
} catch (error) {
this.agora.handleError(error)
}
}
}

module.exports = Ballots
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const Agora = require('./Agora')
module.exports = new Agora()
127 changes: 127 additions & 0 deletions packages/ethernaut-optigov/src/tasks/Ballots.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
const types = require('ethernaut-common/src/validation/types')
const output = require('ethernaut-common/src/ui/output')
const Ballots = require('../internal/agora/Ballots')
const agora = require('../internal/agora/agoraInstance')

require('../scopes/optigov')
.task('ballots', 'Interacts with ballots on Agora: retrieve or submit')
.addParam(
'action',
'Action to perform: "get" or "submit"',
undefined,
types.string,
)
.addParam('roundId', 'RetroFunding round ID', undefined, types.string)
.addParam(
'ballotContent',
'Ballot content as a JSON string (optional for submit)',
undefined,
types.string,
)

.setAction(async ({ action, roundId, ballotContent }, hre) => {
try {
const ballots = new Ballots(agora)

if (action === 'get') {
const signers = await hre.ethers.getSigners()
if (!signers.length) {
throw new Error('No signers available.')
}
const signer = signers[0]
const ballot = await ballots.getBallot(roundId, signer.address)
// Format the ballot output if it's an object
const ballotResult =
typeof ballot === 'string' ? ballot : formatBallot(ballot)
return output.resultBox(
ballotResult,
`Ballot for Round ${roundId} & ${signer.address}`,
)
} else if (action === 'submit') {
const signers = await hre.ethers.getSigners()
if (!signers.length) {
throw new Error('No signers available.')
}
const signer = signers[0]
// Prepare content to be signed
const defaultContent = {
allocations: [{}],
os_only: true,
os_multiplier: 0,
}
const contentToProcess = ballotContent
? ballotContent
: JSON.stringify(defaultContent)
const parsedContent = ballotContent
? JSON.parse(ballotContent)
: defaultContent
const computedSignature = await signer.signMessage(contentToProcess)

const payload = {
address: signer.address,
ballot_content: parsedContent,
signature: computedSignature,
}
const result = await ballots.submitBallot(
roundId,
signer.address,
payload,
)
const resultString =
typeof result === 'string' ? result : JSON.stringify(result)
return output.resultBox(
resultString,
`Submit Ballot for Round ${roundId} & ${signer.address}`,
)
} else {
throw new Error('Invalid action. Use "get" or "submit".')
}
} catch (err) {
return output.errorBox(err)
}
})

// Utility: formats the ballot object for display
function formatBallot(ballot) {
let formatted = ''
formatted += `Address: ${ballot.address}\n`
formatted += `Round ID: ${ballot.round_id}\n`
formatted += `Status: ${ballot.status}\n`
formatted += `Budget: ${ballot.budget}\n`
formatted += `Created At: ${ballot.created_at}\n`
formatted += `Updated At: ${ballot.updated_at}\n`
formatted += `Published At: ${ballot.published_at}\n\n`

if (Array.isArray(ballot.category_allocations)) {
formatted += 'Category Allocations:\n'
ballot.category_allocations.forEach((cat) => {
formatted += ` - Category Slug: ${cat.category_slug}, Allocation: ${cat.allocation}, Locked: ${cat.locked}\n`
})
formatted += '\n'
}

if (Array.isArray(ballot.projects_allocations)) {
formatted += 'Projects Allocations:\n'
ballot.projects_allocations.forEach((proj) => {
formatted += ` - Project ID: ${proj.project_id}, Name: ${proj.name}, Image: ${proj.image}, Position: ${proj.position}, Allocation: ${proj.allocation}, Impact: ${proj.impact}\n`
})
formatted += '\n'
}

if (Array.isArray(ballot.projects_to_be_evaluated)) {
formatted += 'Projects to be Evaluated:\n'
ballot.projects_to_be_evaluated.forEach((project) => {
formatted += ` - ${project}\n`
})
formatted += '\n'
}

if (ballot.payload_for_signature) {
formatted += 'Payload for Signature:\n'
formatted += ` Budget: ${ballot.payload_for_signature.budget}\n`
formatted += ` Category Allocation: ${JSON.stringify(ballot.payload_for_signature.category_allocation, null, 2)}\n`
formatted += ` Projects Allocation: ${JSON.stringify(ballot.payload_for_signature.projects_allocation, null, 2)}\n`
}

return formatted
}
4 changes: 1 addition & 3 deletions packages/ethernaut-optigov/src/tasks/Delegates.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const types = require('ethernaut-common/src/validation/types')
const output = require('ethernaut-common/src/ui/output')
const Delegates = require('../internal/agora/Delegates')
const Agora = require('../internal/agora/Agora')
const agora = require('../internal/agora/agoraInstance')

const RELATED_DATA = {
votes: 'votes',
Expand Down Expand Up @@ -40,7 +40,6 @@ require('../scopes/optigov')
)
.setAction(async ({ limit, offset, address, relatedData }) => {
try {
const agora = new Agora()
const delegates = new Delegates(agora)

if (address) {
Expand Down Expand Up @@ -78,7 +77,6 @@ require('../scopes/optigov')

// If no specific address or ENS is given, fetch the list of delegates
const delegateList = await delegates.getDelegates({ limit, offset })

return output.resultBox(printDelegates(delegateList), 'Delegates')
} catch (err) {
return output.errorBox(err)
Expand Down
3 changes: 1 addition & 2 deletions packages/ethernaut-optigov/src/tasks/Projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const types = require('ethernaut-common/src/validation/types')
const output = require('ethernaut-common/src/ui/output')
const Projects = require('../internal/agora/Projects')
const Rounds = require('../internal/agora/Rounds')
const Agora = require('../internal/agora/Agora')
const agora = require('../internal/agora/agoraInstance')

require('../scopes/optigov')
.task(
Expand Down Expand Up @@ -81,7 +81,6 @@ function printProjects(projects) {
}

async function getProjects(limit, offset, round) {
const agora = new Agora()
const projects = new Projects(agora)
const rounds = new Rounds(agora)

Expand Down
3 changes: 1 addition & 2 deletions packages/ethernaut-optigov/src/tasks/Proposals.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const types = require('ethernaut-common/src/validation/types')
const output = require('ethernaut-common/src/ui/output')
const Proposals = require('../internal/agora/Proposals')
const Agora = require('../internal/agora/Agora')
const agora = require('../internal/agora/agoraInstance')

const VOTES = {
yes: 'yes',
Expand Down Expand Up @@ -40,7 +40,6 @@ require('../scopes/optigov')
.setAction(async ({ limit, offset, proposalId, votes }) => {
try {
// Instantiate Agora and Proposals
const agora = new Agora()
const proposals = new Proposals(agora)

// If proposalId is provided, fetch specific proposal or votes
Expand Down
3 changes: 1 addition & 2 deletions packages/ethernaut-optigov/src/tasks/Rounds.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const types = require('ethernaut-common/src/validation/types')
const output = require('ethernaut-common/src/ui/output')
const Rounds = require('../internal/agora/Rounds')
const Agora = require('../internal/agora/Agora')
const agora = require('../internal/agora/agoraInstance')

const LATEST = {
yes: 'yes',
Expand Down Expand Up @@ -39,7 +39,6 @@ require('../scopes/optigov')
)
.setAction(async ({ limit, offset, roundId, latest }) => {
try {
const agora = new Agora()
const rounds = new Rounds(agora)

if (latest === LATEST.yes) {
Expand Down
5 changes: 2 additions & 3 deletions packages/ethernaut-optigov/src/tasks/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const output = require('ethernaut-common/src/ui/output')
const { createSiweMessage } = require('../internal/Siwe')
const EthernautCliError = require('ethernaut-common/src/error/error')
const Auth = require('../internal/agora/Auth')
const Agora = require('../internal/agora/Agora')
const agora = require('../internal/agora/agoraInstance')

require('../scopes/optigov')
.task(
Expand All @@ -25,7 +25,6 @@ require('../scopes/optigov')
'ethernaut-optigov',
)

const agora = new Agora()
const auth = new Auth(agora)

const statement = 'Log in to Agoras RetroPGF API with SIWE.'
Expand All @@ -39,7 +38,7 @@ require('../scopes/optigov')

await auth.authenticateWithAgora(preparedMessage, signature, nonce)

return output.resultBox(`Logged in with address: ${signer.address})`)
return output.resultBox(`Logged in with address: ${signer.address}`)
} catch (err) {
return output.errorBox(err)
}
Expand Down
34 changes: 34 additions & 0 deletions packages/ethernaut-optigov/src/tasks/setAgoraKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const types = require('ethernaut-common/src/validation/types')
const output = require('ethernaut-common/src/ui/output')
const storage = require('ethernaut-common/src/io/storage')
const { setEnvVar } = require('ethernaut-common/src/io/env')

require('../scopes/optigov')
.task('agorakey', 'Sets the Agora API Key')
.addParam('apiKey', 'The Agora API Key to use', undefined, types.string)
.setAction(async ({ apiKey }, _hre) => {
try {
const config = storage.readConfig()

let summary = []

if (apiKey) {
const currentKey = process.env.AGORA_API_KEY
setEnvVar('AGORA_API_KEY', apiKey)
summary.push(`- Agora API Key set to ${apiKey} (was ${currentKey})`)
summary.push(
'Please restart the tool for the new API key to take effect.',
)
}

storage.saveConfig(config)

if (summary.length === 0) {
summary.push('No changes')
}

return output.resultBox(summary.join('\n'))
} catch (err) {
return output.errorBox(err)
}
})
Loading
Loading