diff --git a/packages/ethernaut-zeronaut/src/internal/connect.js b/packages/ethernaut-zeronaut/src/internal/connect.js index a5e68aa..1a509bf 100644 --- a/packages/ethernaut-zeronaut/src/internal/connect.js +++ b/packages/ethernaut-zeronaut/src/internal/connect.js @@ -29,6 +29,21 @@ function _getGameAbi(network) { return JSON.parse(fs.readFileSync(abiPath, 'utf8')).abi } +async function getLevelContract(hre, address) { + const abi = _getLevelAbi() + return await hre.ethers.getContractAt(abi, address) +} + +function _getLevelAbi() { + const artifactsFolderPath = path.join( + _getArtifactsFolderPath(), + 'contracts', + 'interfaces', + ) + const abiPath = path.join(artifactsFolderPath, 'ILevel.sol', 'ILevel.json') + return JSON.parse(fs.readFileSync(abiPath, 'utf8')).abi +} + function _getDeployedAddresses(network) { const addressesPath = path.join( _getNetworkFolderPath(network), @@ -37,6 +52,10 @@ function _getDeployedAddresses(network) { return JSON.parse(fs.readFileSync(addressesPath, 'utf8')) } +function _getArtifactsFolderPath() { + return path.join(_getZeronautFolderPath(), 'artifacts') +} + function _getNetworkFolderPath(network) { const networkFolderPath = path.join(_getIgnitionFolderPath(), network) const deploymentExists = fs.existsSync(networkFolderPath) @@ -47,9 +66,13 @@ function _getNetworkFolderPath(network) { } function _getIgnitionFolderPath() { - const zeronautPkgPath = require.resolve('zeronaut/package.json') - const zeronautDir = path.dirname(zeronautPkgPath) + const zeronautDir = _getZeronautFolderPath() return path.join(zeronautDir, 'ignition', 'deployments') } -module.exports = { connect } +function _getZeronautFolderPath() { + const zeronautPkgPath = require.resolve('zeronaut/package.json') + return path.dirname(zeronautPkgPath) +} + +module.exports = { connect, getLevelContract } diff --git a/packages/ethernaut-zeronaut/src/tasks/get-campaign.js b/packages/ethernaut-zeronaut/src/tasks/get-campaign.js index 4331414..6d3d59f 100644 --- a/packages/ethernaut-zeronaut/src/tasks/get-campaign.js +++ b/packages/ethernaut-zeronaut/src/tasks/get-campaign.js @@ -22,13 +22,7 @@ require('../scopes/zeronaut') let str = '' str += `\n name: ${hre.ethers.decodeBytes32String(campaign.id)}` - str += `\n levels: ${campaign.levels.length}` - - for (let i = 0; i < campaign.levels.length; i++) { - const levelAddress = campaign.levels[i] - const levelName = await contract.getLevelName(levelAddress) - str += `\n - Level ${i}: "${hre.ethers.decodeBytes32String(levelName)}"` - } + str += `\n owner: ${campaign.owner}` return output.resultBox(str) } catch (err) { diff --git a/packages/ethernaut-zeronaut/src/tasks/get-level.js b/packages/ethernaut-zeronaut/src/tasks/get-level.js index b1f4d8f..d6b2d5b 100644 --- a/packages/ethernaut-zeronaut/src/tasks/get-level.js +++ b/packages/ethernaut-zeronaut/src/tasks/get-level.js @@ -1,39 +1,38 @@ const types = require('ethernaut-common/src/validation/types') const output = require('ethernaut-common/src/ui/output') const { getChainId } = require('ethernaut-common/src/util/network') -const { connect } = require('../internal/connect') +const { connect, getLevelContract } = require('../internal/connect') require('../scopes/zeronaut') .task('get-level', 'Retrieves details for a level') .addPositionalParam( 'name', - 'The name of the campaign', + 'The name of the level to get', undefined, types.string, ) - .addPositionalParam( - 'level', - 'The index of level to get', - undefined, - types.string, - ) - .setAction(async ({ name, level }, hre) => { + .setAction(async ({ name }, hre) => { try { + // Connect to the game contract const chainId = await getChainId(hre) const contract = await connect(`chain-${chainId}`, hre) + // Retrieve the level address const id = hre.ethers.encodeBytes32String(name) - const campaign = await contract.getCampaign(id) + const levelData = await contract.getLevel(id) + const levelAddress = levelData.addr + + // Connect to the level contract + const level = await getLevelContract(hre, levelAddress) - const levelAddress = campaign.levels[level] - const levelName = await contract.getLevelName(levelAddress) - const levelInstructions = - await contract.getLevelInstructions(levelAddress) + // Query the level details + const levelName = await level.name() + const levelInstructions = await level.instructions() + // Display the level details let str = '' str += ` name: ${hre.ethers.decodeBytes32String(levelName)}` str += `\n instructions: ${levelInstructions}` - return output.resultBox(str) } catch (err) { return output.errorBox(err) diff --git a/packages/ethernaut-zeronaut/src/tasks/play-level.js b/packages/ethernaut-zeronaut/src/tasks/play-level.js index 0a1cd66..e1e600d 100644 --- a/packages/ethernaut-zeronaut/src/tasks/play-level.js +++ b/packages/ethernaut-zeronaut/src/tasks/play-level.js @@ -1,36 +1,33 @@ const types = require('ethernaut-common/src/validation/types') const output = require('ethernaut-common/src/ui/output') const { getChainId } = require('ethernaut-common/src/util/network') -const { connect } = require('../internal/connect') +const { connect, getLevelContract } = require('../internal/connect') const { prompt } = require('ethernaut-common/src/ui/prompt') require('../scopes/zeronaut') .task('play-level', 'Plays a level') .addPositionalParam( - 'campaign', - 'The name of the campaign', + 'name', + 'The name of level to play', undefined, types.string, ) - .addPositionalParam( - 'level', - 'The index of level to play', - undefined, - types.string, - ) - .setAction(async ({ campaign, level }, hre) => { + .setAction(async ({ name }, hre) => { try { // Retrieve the game contract const chainId = await getChainId(hre) const contract = await connect(`chain-${chainId}`, hre) - // Retrieve the campaign - const id = hre.ethers.encodeBytes32String(campaign) - const campaignData = await contract.getCampaign(id) + // Retrieve the level address + const levelId = hre.ethers.encodeBytes32String(name) + const levelData = await contract.getLevel(levelId) + const levelAddress = levelData.addr + + // Connect to the level contract + const level = await getLevelContract(hre, levelAddress) - // Retrieve the level - const levelAddress = campaignData.levels[level] - const levelCircuitData = await contract.getLevelCircuit(levelAddress) + // Retrieve the level circuit + const levelCircuitData = await level.circuit() // Build the circuit and collect the parameters const circuit = JSON.parse(levelCircuitData) @@ -42,12 +39,11 @@ require('../scopes/zeronaut') const proof = await _buildProof(circuit, inputs) // console.log('proof', proof) - // Submit the proof to the contract - const success = await contract.checkLevel( - levelAddress, - proof, - publicInputs, - ) + // Check the proof + const success = await level.check(proof, publicInputs) + + // Submit the proof + // TODO return output.resultBox(`Success: ${success}`) } catch (err) { diff --git a/packages/ethernaut-zeronaut/src/tasks/create-level.js b/packages/ethernaut-zeronaut/src/tasks/set-level.js similarity index 54% rename from packages/ethernaut-zeronaut/src/tasks/create-level.js rename to packages/ethernaut-zeronaut/src/tasks/set-level.js index 2e6bbfb..d9ac272 100644 --- a/packages/ethernaut-zeronaut/src/tasks/create-level.js +++ b/packages/ethernaut-zeronaut/src/tasks/set-level.js @@ -4,29 +4,33 @@ const { getChainId } = require('ethernaut-common/src/util/network') const { connect } = require('../internal/connect') require('../scopes/zeronaut') - .task('create-level', 'Adds a level to a campaign') - .addPositionalParam( - 'level', - 'The address of the level', - undefined, - types.address, - ) + .task('set-level', 'Assigns a level to a campaign') .addPositionalParam( 'campaign', 'The name of the campaign', undefined, types.string, ) - .setAction(async ({ campaign, level }, hre) => { + .addPositionalParam('name', 'The name of the level', undefined, types.string) + .addPositionalParam( + 'address', + 'The address of the level', + undefined, + types.address, + ) + .setAction(async ({ campaign, name, address }, hre) => { try { + // Connect to the game contract const chainId = await getChainId(hre) const contract = await connect(`chain-${chainId}`, hre) - const id = hre.ethers.encodeBytes32String(campaign) - const tx = await contract.createLevel(id, level) + // Set the level for the campaign + const campaignId = hre.ethers.encodeBytes32String(campaign) + const levelId = hre.ethers.encodeBytes32String(name) + const tx = await contract.setLevel(campaignId, levelId, address) await tx.wait() - return output.resultBox(`Added level ${level} to campaign ${campaign}`) + return output.resultBox(`Added level ${name} to campaign ${campaign}`) } catch (err) { return output.errorBox(err) }