circuitscan
Advanced tools
Comparing version 0.0.2-alpha to 0.0.3-alpha
12
cli.js
#!/usr/bin/env node | ||
import {Command} from 'commander'; | ||
import {verify} from './index.js'; | ||
import {verify, deploy} from './index.js'; | ||
@@ -23,4 +23,12 @@ const program = new Command(); | ||
// TODO command to deploy a circuit verifier, verify its contract and circuit | ||
program | ||
.command('deploy <mainCircomFile> <chainId>') | ||
.description('Deploy verifier contract by their circom sources. Can also specify chain by name.') | ||
.option('-p, --protocol <protocol>', 'Specify the protocol: groth16 (default), fflonk, plonk (overrides circomkit.json if available)') | ||
.option('-k, --proving-key <provingKey>', 'Specify the proving key url (optional, for Groth16 trusted setups)') | ||
.option('-v, --circom-version <circomVersion>', 'Specify the Circom version (e.g. "v2.1.8")') | ||
.option('-i, --instance <memorySize>', 'Specify the memory (GB) of compiler instance: 4 for testing (default: 10GB lambda, faster init for small circuits)') | ||
.option('-l, --localhost <localPort>', 'Use a circom compiler container running on a specific port') | ||
.action(deploy); | ||
program.parse(process.argv); |
89
index.js
import {relative, dirname} from 'node:path'; | ||
import { isHex } from 'viem'; | ||
import loadCircom from './src/loadCircom.js'; | ||
import {findChainByName} from './src/chains.js'; | ||
import {findChain} from './src/chains.js'; | ||
import {generateRandomString, fetchJson, delay, instanceSizes} from './src/utils.js'; | ||
import {StatusLogger} from './src/StatusLogger.js'; | ||
import { | ||
compileContract, | ||
deployContract, | ||
verifyOnEtherscan, | ||
checkEtherscanStatus, | ||
} from './src/deployer.js'; | ||
@@ -12,11 +19,62 @@ const defaultCircomPath = 'circom-v2.1.8'; | ||
const circomCompilerURL = 'https://uvopzbfbfz5i5m4i3tsgq7rjeu0glwdl.lambda-url.us-west-2.on.aws/'; | ||
const statusURL = 'https://blob.circuitscan.org/status/'; | ||
const stackStarterURL = 'https://fydvjclemuhxdzsv2treynl32q0rwtpp.lambda-url.us-west-2.on.aws/'; | ||
const blobUrl = 'https://blob.circuitscan.org/'; | ||
export async function verify(file, chainId, contractAddr, options) { | ||
if(isNaN(chainId)) { | ||
const chain = findChainByName(chainId); | ||
if(!chain) throw new Error('invalid_chain'); | ||
chainId = chain.chain.id; | ||
const chain = findChain(chainId); | ||
if(!chain) throw new Error('invalid_chain'); | ||
const {curCompilerURL, stackId} = await determineCompilerUrl(options); | ||
try { | ||
const compiled = await compileFile(file, options, { curCompilerURL }); | ||
await verifyCircuit(compiled.pkgName, chain.chain.id, contractAddr, options); | ||
} catch(error) { | ||
console.error(error); | ||
} | ||
if(stackId) { | ||
await stopInstance(stackId); | ||
} | ||
} | ||
export async function deploy(file, chainId, options) { | ||
const chain = findChain(chainId); | ||
if(!chain) throw new Error('invalid_chain'); | ||
const privateKey = process.env.DEPLOYER_PRIVATE_KEY; | ||
if(!privateKey || !isHex(privateKey) || privateKey.length !== 66) | ||
throw new Error('INVALID_DEPLOYER_PRIVATE_KEY') | ||
if(!(chain.apiKeyEnvVar in process.env)) | ||
throw new Error('MISSING_' + chain.apiKeyEnvVar); | ||
const {curCompilerURL, stackId} = await determineCompilerUrl(options); | ||
let instanceRunning = !!stackId; | ||
try { | ||
const compiled = await compileFile(file, options, { curCompilerURL }); | ||
if(stackId) { | ||
await stopInstance(stackId); | ||
instanceRunning = false; | ||
} | ||
const contractSource = await (await fetch(`${blobUrl}${compiled.pkgName}/verifier.sol`)).text(); | ||
const solcOutput = compileContract(contractSource); | ||
const contractAddress = await deployContract(solcOutput, chain.chain, privateKey); | ||
let verifyResult = false; | ||
while(!verifyResult || verifyResult.result.startsWith('Unable to locate ContractCode')) { | ||
await delay(5000); | ||
verifyResult = await verifyOnEtherscan(chain, contractAddress, contractSource, solcOutput.version); | ||
} | ||
if(!verifyResult.status === '1') throw new Error('UNABLE_TO_VERIFY_CONTRACT'); | ||
let contractStatus = {}; | ||
console.log(`# Waiting for verification on Etherscan...`); | ||
while(['Already Verified', 'Pass - Verified'].indexOf(contractStatus.result) === -1) { | ||
await delay(5000); | ||
contractStatus = await checkEtherscanStatus(chain, verifyResult.result); | ||
console.log(`> ${contractStatus.result}`); | ||
} | ||
await verifyCircuit(compiled.pkgName, chain.chain.id, contractAddress, options); | ||
} catch(error) { | ||
console.error(error); | ||
} | ||
if(stackId && instanceRunning) { | ||
await stopInstance(stackId); | ||
} | ||
} | ||
async function determineCompilerUrl(options) { | ||
let curCompilerURL = circomCompilerURL; | ||
@@ -31,12 +89,3 @@ let stackId; | ||
} | ||
const compiled = await compileFile(file, options, { curCompilerURL }); | ||
const verified = await verifyCircuit(compiled.pkgName, chainId, contractAddr, options); | ||
if(verified && verified.status === 'verified') { | ||
console.log(`# Completed successfully!`); | ||
} else { | ||
console.log(`# Verification failed.`); | ||
} | ||
if(stackId) { | ||
await stopInstance(stackId); | ||
} | ||
return {curCompilerURL, stackId}; | ||
} | ||
@@ -125,3 +174,3 @@ | ||
// status report during compilation | ||
const status = new StatusLogger(`${statusURL}${requestId}.json`, 3000); | ||
const status = new StatusLogger(`${blobUrl}status/${requestId}.json`, 3000); | ||
@@ -198,2 +247,8 @@ const event = { | ||
if(body && body.status === 'verified') { | ||
console.log(`# Completed successfully!`); | ||
console.log(`\nhttps://circuitscan.org/chain/${chainId}/address/${contractAddr}`); | ||
} else { | ||
console.log(`# Verification failed.`); | ||
} | ||
return body; | ||
@@ -200,0 +255,0 @@ } |
{ | ||
"name": "circuitscan", | ||
"version": "0.0.2-alpha", | ||
"version": "0.0.3-alpha", | ||
"main": "index.js", | ||
@@ -25,4 +25,5 @@ "type": "module", | ||
"commander": "^12.0.0", | ||
"solc": "^0.8.26", | ||
"viem": "^2.12.5" | ||
} | ||
} |
@@ -74,6 +74,58 @@ # Circuitscan CLI | ||
NYI! | ||
``` | ||
Usage: circuitscan deploy [options] <mainCircomFile> <chainId> | ||
Deploy verifier contract by their circom sources. Can also specify chain by name. | ||
Options: | ||
-p, --protocol <protocol> Specify the protocol: groth16 (default), fflonk, plonk (overrides circomkit.json if available) | ||
-k, --proving-key <provingKey> Specify the proving key url (optional, for Groth16 trusted setups) | ||
-v, --circom-version <circomVersion> Specify the Circom version (e.g. "v2.1.8") | ||
-i, --instance <memorySize> Specify the memory (GB) of compiler instance: 4 for testing (default: 10GB lambda, faster init for small circuits) | ||
-l, --localhost <localPort> Use a circom compiler container running on a specific port | ||
-h, --help display help for command | ||
``` | ||
> [!IMPORTANT] | ||
> `DEPLOYER_PRIVATE_KEY` environment variable and a corresponding Etherscan API key is required | ||
| name | apiKeyEnvVar | | ||
|--------------|---------------------------| | ||
| holesky | ETHERSCAN_API_KEY | | ||
| sepolia | ETHERSCAN_API_KEY | | ||
| mainnet | ETHERSCAN_API_KEY | | ||
| optimism | OPTIMISM_ETHERSCAN_API_KEY| | ||
| polygon | POLYGON_ETHERSCAN_API_KEY | | ||
| fantom | FANTOM_ETHERSCAN_API_KEY | | ||
| arbitrum | ARBITRUM_ETHERSCAN_API_KEY| | ||
| arbitrumNova | ARBITRUM_NOVA_ETHERSCAN_API_KEY| | ||
| gnosis | GNOSIS_ETHERSCAN_API_KEY | | ||
| celo | CELO_ETHERSCAN_API_KEY | | ||
| base | BASE_ETHERSCAN_API_KEY | | ||
Example usage using `.env` for configuration: | ||
``` | ||
$ dotenv run circuitscan deploy circuits/multiplier.circom polygon | ||
Found 1 file(s): | ||
multiplier.circom | ||
> Compiling multiplier-worried-aqua-roundworm... | ||
> Downloading PTAU... @ 0.0207s | ||
> Groth16 setup with random entropy... @ 0.0211s | ||
> Exporting verification key and solidity verifier... @ 0.0676s | ||
> Storing build artifacts... @ 0.0860s | ||
# Sent transaction 0x5b208fa766f744840fcf3827b7f2573f2ab1ec03c200c294dd6c73c98c6108f2 | ||
# Deployed to 0x269e831b930f4c1ec7eee28aa53e5756b0f96d0c | ||
# Waiting for verification on Etherscan... | ||
> Pass - Verified | ||
# Verifying circuit... | ||
# Completed successfully! | ||
https://circuitscan.org/chain/137/address/0x269e831b930f4c1ec7eee28aa53e5756b0f96d0c | ||
``` | ||
## License | ||
MIT |
@@ -16,2 +16,5 @@ import { | ||
export function findChain(chainId) { | ||
if(isNaN(chainId)) { | ||
return findChainByName(chainId); | ||
} | ||
for(let chain of chains) { | ||
@@ -22,3 +25,3 @@ if(Number(chainId) === chain.chain.id) return chain; | ||
export function findChainByName(name) { | ||
function findChainByName(name) { | ||
for(let chain of chains) { | ||
@@ -29,3 +32,2 @@ if(name === chain.name) return chain; | ||
export const chains = [ | ||
@@ -36,3 +38,4 @@ { | ||
apiUrl: 'https://api-holesky.etherscan.io/api', | ||
apiKey: process.env.ETHERSCAN_API_KEY | ||
apiKey: process.env.ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'ETHERSCAN_API_KEY' | ||
}, | ||
@@ -43,3 +46,4 @@ { | ||
apiUrl: 'https://api-sepolia.etherscan.io/api', | ||
apiKey: process.env.ETHERSCAN_API_KEY | ||
apiKey: process.env.ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'ETHERSCAN_API_KEY' | ||
}, | ||
@@ -50,3 +54,4 @@ { | ||
apiUrl: 'https://api.etherscan.io/api', | ||
apiKey: process.env.ETHERSCAN_API_KEY | ||
apiKey: process.env.ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'ETHERSCAN_API_KEY' | ||
}, | ||
@@ -57,3 +62,4 @@ { | ||
apiUrl: 'https://api-optimistic.etherscan.io/api', | ||
apiKey: process.env.OPTIMISM_ETHERSCAN_API_KEY | ||
apiKey: process.env.OPTIMISM_ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'OPTIMISM_ETHERSCAN_API_KEY' | ||
}, | ||
@@ -64,3 +70,4 @@ { | ||
apiUrl: 'https://api.polygonscan.com/api', | ||
apiKey: process.env.POLYGON_ETHERSCAN_API_KEY | ||
apiKey: process.env.POLYGON_ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'POLYGON_ETHERSCAN_API_KEY' | ||
}, | ||
@@ -71,3 +78,4 @@ { | ||
apiUrl: 'https://api.ftmscan.com/api', | ||
apiKey: process.env.FANTOM_ETHERSCAN_API_KEY | ||
apiKey: process.env.FANTOM_ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'FANTOM_ETHERSCAN_API_KEY' | ||
}, | ||
@@ -78,3 +86,4 @@ { | ||
apiUrl: 'https://api.arbiscan.io/api', | ||
apiKey: process.env.ARBITRUM_ETHERSCAN_API_KEY | ||
apiKey: process.env.ARBITRUM_ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'ARBITRUM_ETHERSCAN_API_KEY' | ||
}, | ||
@@ -85,3 +94,4 @@ { | ||
apiUrl: 'https://api.arbiscan.io/api', | ||
apiKey: process.env.ARBITRUM_NOVA_ETHERSCAN_API_KEY | ||
apiKey: process.env.ARBITRUM_NOVA_ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'ARBITRUM_NOVA_ETHERSCAN_API_KEY' | ||
}, | ||
@@ -92,3 +102,4 @@ { | ||
apiUrl: 'https://api.gnosisscan.io/api', | ||
apiKey: process.env.GNOSIS_ETHERSCAN_API_KEY | ||
apiKey: process.env.GNOSIS_ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'GNOSIS_ETHERSCAN_API_KEY' | ||
}, | ||
@@ -99,3 +110,4 @@ { | ||
apiUrl: 'https://api.celoscan.io/api', | ||
apiKey: process.env.CELO_ETHERSCAN_API_KEY | ||
apiKey: process.env.CELO_ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'CELO_ETHERSCAN_API_KEY' | ||
}, | ||
@@ -106,4 +118,6 @@ { | ||
apiUrl: 'https://api.basescan.org/api', | ||
apiKey: process.env.BASE_ETHERSCAN_API_KEY | ||
apiKey: process.env.BASE_ETHERSCAN_API_KEY, | ||
apiKeyEnvVar: 'BASE_ETHERSCAN_API_KEY' | ||
}, | ||
]; | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
35223
20
824
131
3
13
7
+ Addedsolc@^0.8.26
+ Addedcommand-exists@1.2.9(transitive)
+ Addedcommander@8.3.0(transitive)
+ Addedfollow-redirects@1.15.9(transitive)
+ Addedjs-sha3@0.8.0(transitive)
+ Addedmemorystream@0.3.1(transitive)
+ Addedos-tmpdir@1.0.2(transitive)
+ Addedsemver@5.7.2(transitive)
+ Addedsolc@0.8.28(transitive)
+ Addedtmp@0.0.33(transitive)