Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
ff7214e
Merge pull request #36 from eternauta1337/optigov
eternauta1337 Oct 23, 2024
3ca950f
refactor: Add Projects class and tasks for ethernaut-optigov plugin
luisvid Oct 22, 2024
d3677ca
refactor: Add Agora class and related modules for ethernaut-optigov p…
luisvid Oct 22, 2024
591c769
refactor: Update Agora class and related modules Auth & Projects
luisvid Oct 24, 2024
ecb9c2c
refactor: Add Projects class and tasks for ethernaut-optigov plugin
luisvid Oct 22, 2024
3561079
refactor: Add Agora class and related modules for ethernaut-optigov p…
luisvid Oct 22, 2024
65e422e
refactor: Update Agora class and related modules Auth & Projects
luisvid Oct 24, 2024
2c14e1c
feat: new proposals task
luisvid Oct 24, 2024
fa08775
refactor: Proposals priunt results
luisvid Oct 25, 2024
776718a
refactor: Add optional parameter for proposalId in Proposals.js
luisvid Oct 26, 2024
2c407fe
test: add test from proposals task
luisvid Oct 26, 2024
7104315
refactor: add pagination for projects
luisvid Oct 26, 2024
7a94efc
test: add proejcts tests
luisvid Oct 26, 2024
9818ac0
refactor: Add ethernaut-optigov workflow to CI configuration
luisvid Oct 26, 2024
1b426fa
feat: add optigov delegates task
luisvid Oct 28, 2024
e50a168
refactor: getLatestRound moved to Rounds class
luisvid Nov 5, 2024
58a0ac5
feat: add Rounds task
luisvid Nov 5, 2024
a0ff516
Expands coverage on test
raiseerco Mar 31, 2025
18c5d07
Adds more tests
raiseerco Mar 31, 2025
d717fc2
test: add unit tests for Agora, Auth, and Projects modules
luisvid Mar 31, 2025
36cffe7
Updates test on update
raiseerco Mar 31, 2025
6676be8
test: enhance error handling and add new tests for Agora and Auth mod…
luisvid Mar 31, 2025
db64fb6
refactor: add pagination for projects
luisvid Oct 26, 2024
3ab3291
test: add proejcts tests
luisvid Oct 26, 2024
d02342d
Expands coverage on test
raiseerco Mar 31, 2025
a973169
Adds more tests
raiseerco Mar 31, 2025
2659fd4
test: add unit tests for Agora, Auth, and Projects modules
luisvid Mar 31, 2025
d744195
Updates test on update
raiseerco Mar 31, 2025
7e8083c
test: enhance error handling and add new tests for Agora and Auth mod…
luisvid Mar 31, 2025
11f21ff
test: add unit tests for Proposals module with error handling
luisvid Mar 31, 2025
8926b78
refactor: add pagination for projects
luisvid Oct 26, 2024
078120a
test: add proejcts tests
luisvid Oct 26, 2024
52b346d
Expands coverage on test
raiseerco Mar 31, 2025
7e745a7
Adds more tests
raiseerco Mar 31, 2025
73858b6
test: add unit tests for Agora, Auth, and Projects modules
luisvid Mar 31, 2025
8f72e80
Updates test on update
raiseerco Mar 31, 2025
3894e5e
test: enhance error handling and add new tests for Agora and Auth mod…
luisvid Mar 31, 2025
5bce354
test: add unit tests for Proposals module with error handling
luisvid Mar 31, 2025
47f3aef
test: add unit tests for Delegates module with error handling
luisvid Mar 31, 2025
f9a4d36
refactor: add pagination for projects
luisvid Oct 26, 2024
363c78b
test: add proejcts tests
luisvid Oct 26, 2024
4bb2718
Expands coverage on test
raiseerco Mar 31, 2025
3338154
Adds more tests
raiseerco Mar 31, 2025
5687575
test: add unit tests for Agora, Auth, and Projects modules
luisvid Mar 31, 2025
8c1b11a
Updates test on update
raiseerco Mar 31, 2025
eacaac1
test: enhance error handling and add new tests for Agora and Auth mod…
luisvid Mar 31, 2025
452f957
test: add unit tests for Proposals module with error handling
luisvid Mar 31, 2025
db51052
test: add unit tests for Delegates module with error handling
luisvid Mar 31, 2025
81c04aa
test: add unit tests for Rounds module with error handling
luisvid Mar 31, 2025
89d4cdf
test: remove outdated tests for Projects module and refine error hand…
luisvid Mar 31, 2025
c0c21c3
Update test
raiseerco Mar 31, 2025
962ddd8
Merge pull request #37 from ethernautdao/retro/projects
raiseerco Apr 1, 2025
a1943e4
Fixes test
raiseerco Apr 1, 2025
4b9134d
Merge pull request #40 from ethernautdao/retro/proposals
raiseerco Apr 1, 2025
6ba4e30
chore: trigger CI/CD pipeline
raiseerco Apr 1, 2025
c35a2d6
Merge branch 'main' into feature/optigov
raiseerco Apr 1, 2025
41cd18f
chore: update package version to 1.1.12 and clean up update tests
luisvid Apr 1, 2025
1c65415
Merge pull request #43 from ethernautdao/retro/delegates
luisvid Apr 1, 2025
75e59c7
chore: update package version to 1.1.12 and clean up update tests
luisvid Apr 1, 2025
3aa9027
Merge branch 'feature/optigov' into retro/rounds
luisvid Apr 1, 2025
1a9d66f
test: remove outdated test for fetching projects in the latest round
luisvid Apr 1, 2025
1e3b677
Merge pull request #44 from ethernautdao/retro/rounds
luisvid Apr 1, 2025
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
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ jobs:
github-token: ${{ secrets.COVERALLS_NEW }}
flag-name: ethernaut-oso
parallel: true
ethernaut-optigov:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['20.12.0']
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build --if-present
- run: npm run compile --if-present
- run: cd packages/ethernaut-optigov && npm t
- name: Coveralls
uses: coverallsapp/github-action@v2.3.6
with:
github-token: ${{ secrets.COVERALLS_NEW }}
flag-name: ethernaut-optigov
parallel: true
ethernaut-util:
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -386,6 +407,7 @@ jobs:
[
ethernaut-common,
ethernaut-oso,
ethernaut-optigov,
ethernaut-util,
ethernaut-util-ui,
ethernaut-ui,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ethernaut-cli-monorepo",
"version": "1.0.0",
"version": "1.1.12",
"private": true,
"workspaces": [
"packages/*"
Expand Down
5 changes: 2 additions & 3 deletions packages/ethernaut-cli/test/update.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ describe('update', function () {
before('cache', async function () {
const config = storage.readConfig()
cachedAutoUpdate = config.general?.autoUpdate

cachedPkg = fs.readFileSync('package.json', 'utf8')
})

after('restore', async function () {
const config = storage.readConfig()
config.general.autoUpdate = cachedAutoUpdate
storage.saveConfig(config)

fs.writeFileSync('package.json', cachedPkg, 'utf8')
})

Expand Down Expand Up @@ -69,6 +67,7 @@ describe('update', function () {
describe('when auto update is the current version', function () {
before('modify', async function () {
const pkg = JSON.parse(cachedPkg)
pkg.version = '0.0.0'
const config = storage.readConfig()
config.general.autoUpdate = pkg.version
storage.saveConfig(config)
Expand All @@ -79,7 +78,7 @@ describe('update', function () {
})

it('displays navigation', async function () {
terminal.has('Pick a task or scope')
terminal.has('A new version of the ethernaut-cli is available')
})
})

Expand Down
4 changes: 4 additions & 0 deletions packages/ethernaut-optigov/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ This plugin doesn't depend on any other plugins.
This plugin adds the tasks listed below.

- login Logs in to the Agora RetroPGF API with SIWE (Sign in with Ethereum)
- projects Prints a list of projects registered in RetroPGF, given specified filters
- proposals Prints a list of proposals registered in RetroPGF, given specified filters
- delegate Prints a list of delegates on Agora, or details a specific delegate, with optional related data (votes or delegators)


## Environment extensions

Expand Down
62 changes: 62 additions & 0 deletions packages/ethernaut-optigov/src/internal/agora/Agora.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const axios = require('axios')
const EthernautCliError = require('ethernaut-common/src/error/error')
const debug = require('ethernaut-common/src/ui/debug')

const API_BASE_URL = 'https://vote.optimism.io/api/v1'
const AGORA_API_KEY = process.env.AGORA_API_KEY

class Agora {
constructor() {
this.apiKey = AGORA_API_KEY
this.apiBaseUrl = API_BASE_URL
this.bearerToken = null
}

// Axios instance setup
createAxiosInstance() {
const headers = {
Authorization: this.bearerToken
? `Bearer ${this.bearerToken}`
: `Bearer ${this.apiKey}`,
}

return axios.create({
baseURL: this.apiBaseUrl,
headers,
})
}

// Handle common API errors
handleError(error) {
if (error.response) {
throw new EthernautCliError(
'ethernaut-optigov',
`Http status error: ${error.response.data}`,
)
} else {
throw new EthernautCliError(
'ethernaut-optigov',
`Http status error: ${error.message}`,
)
}
}

// common method for getting API spec
async getSpec() {
try {
const axiosInstance = this.createAxiosInstance()
const response = await axiosInstance.get('/spec')
debug.log(`Spec: ${response.data}`, 'ethernaut-optigov')
return response.data
} catch (error) {
this.handleError(error)
}
}

// Set the bearer token after authentication
setBearerToken(token) {
this.bearerToken = token
}
}

module.exports = Agora
59 changes: 18 additions & 41 deletions packages/ethernaut-optigov/src/internal/agora/Auth.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,38 @@
const axios = require('axios')
const debug = require('ethernaut-common/src/ui/debug')
const EthernautCliError = require('ethernaut-common/src/error/error')
const API_BASE_URL = 'https://vote.optimism.io/api/v1'

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

// Get a nonce from the Agora API
async getNonce() {
try {
const response = await axios.get(`${API_BASE_URL}/auth/nonce`)

const axiosInstance = this.agora.createAxiosInstance()
const response = await axiosInstance.get('/auth/nonce')
debug.log(`Nonce: ${response.data.nonce}`, 'ethernaut-optigov')
return response.data
return response.data.nonce
} catch (error) {
throw new EthernautCliError(
'ethernaut-optigov',
`Http status error: ${error.message}`,
)
this.agora.handleError(error)
}
}

// Authenticate with the Agora API
async authenticateWithAgora(message, signature, nonce) {
try {
const response = await axios.post(
`${API_BASE_URL}/auth/verify`,
{
message,
signature,
nonce,
},
{
headers: {
'Content-Type': 'application/json',
},
},
)
const axiosInstance = this.agora.createAxiosInstance()
const response = await axiosInstance.post('/auth/verify', {
message,
signature,
nonce,
})

debug.log('Auth Response Status:', response.status)
// save the Bearer token
this.bearerToken = response.data.access_token

// Set the Bearer token for future requests
this.agora.setBearerToken(response.data.access_token)

return response.data.access_token
} catch (error) {
if (error.response) {
// Server responded with a status other than 2xx
throw new EthernautCliError(
'ethernaut-optigov',
`Http status error: ${error.response.data}`,
)
} else {
// Other errors (e.g., network issue)
throw new EthernautCliError(
'ethernaut-optigov',
`Http status error: ${error.message}`,
)
}
this.agora.handleError(error)
}
}
}
Expand Down
132 changes: 132 additions & 0 deletions packages/ethernaut-optigov/src/internal/agora/Delegates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
const debug = require('ethernaut-common/src/ui/debug')

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

// Get a list of delegates with pagination
async getDelegates({ limit = 10, offset = 0, sort } = {}) {
try {
const axiosInstance = this.agora.createAxiosInstance()
const response = await axiosInstance.get('/delegates', {
params: { limit, offset, sort },
})

debug.log(`Delegates: ${response.data.data}`, 'ethernaut-optigov')
return response.data.data.map((delegate) => ({
address: delegate.address,
votingPower: delegate.votingPower?.total,
twitter: delegate.statement?.payload?.twitter,
discord: delegate.statement?.payload?.discord,
delegateStatement:
delegate.statement?.payload?.delegateStatement?.substring(0, 100),
}))
} catch (error) {
this.agora.handleError(error)
}
}

// Get a specific delegate by address or ENS name
async getDelegateById(addressOrEnsName) {
try {
const axiosInstance = this.agora.createAxiosInstance()
const response = await axiosInstance.get(`/delegates/${addressOrEnsName}`)

debug.log(`Delegate: ${response.data}`, 'ethernaut-optigov')
const data = response.data
return {
address: data.address,
votingPower: {
advanced: data.votingPower.advanced,
direct: data.votingPower.direct,
total: data.votingPower.total,
},
votingPowerRelativeToVotableSupply:
data.votingPowerRelativeToVotableSupply,
votingPowerRelativeToQuorum: data.votingPowerRelativeToQuorum,
proposalsCreated: data.proposalsCreated,
proposalsVotedOn: data.proposalsVotedOn,
votedFor: data.votedFor,
votedAgainst: data.votedAgainst,
votedAbstain: data.votedAbstain,
votingParticipation: data.votingParticipation,
lastTenProps: data.lastTenProps,
numOfDelegators: data.numOfDelegators,
}
} catch (error) {
this.agora.handleError(error)
}
}

// Get a paginated list of votes for a specific delegate
async getDelegateVotes({
addressOrEnsName,
limit = 10,
offset = 0,
sort,
} = {}) {
try {
const axiosInstance = this.agora.createAxiosInstance()
const response = await axiosInstance.get(
`/delegates/${addressOrEnsName}/votes`,
{
params: { limit, offset, sort },
},
)

debug.log(
`Votes for Delegate ${addressOrEnsName}: ${response.data.data}`,
'ethernaut-optigov',
)
return response.data.data.map((vote) => ({
transactionHash: vote.transactionHash,
proposalId: vote.proposalId,
address: vote.address,
support: vote.support,
reason: vote.reason,
weight: vote.weight,
proposalValue: vote.proposalValue,
proposalTitle: vote.proposalTitle,
proposalType: vote.proposalType,
timestamp: vote.timestamp,
}))
} catch (error) {
this.agora.handleError(error)
}
}

// Get a paginated list of delegators for a specific delegate
async getDelegateDelegators({
addressOrEnsName,
limit = 10,
offset = 0,
sort,
} = {}) {
try {
const axiosInstance = this.agora.createAxiosInstance()
const response = await axiosInstance.get(
`/delegates/${addressOrEnsName}/delegators`,
{
params: { limit, offset, sort },
},
)

debug.log(
`Delegators for Delegate ${addressOrEnsName}: ${response.data.data}`,
'ethernaut-optigov',
)
return response.data.data.map((delegator) => ({
from: delegator.from,
allowance: delegator.allowance,
timestamp: delegator.timestamp,
type: delegator.type,
amount: delegator.amount,
}))
} catch (error) {
this.agora.handleError(error)
}
}
}

module.exports = Delegates
Loading
Loading