-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: verify contracts on filfox #151
base: main
Are you sure you want to change the base?
Changes from all commits
3737a02
9d05fdb
a5ba958
5d4cb80
28cdbfa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,139 @@ | ||
const { execSync } = require('child_process'); | ||
const { writeFileSync } = require('fs'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const axios = require('axios'); | ||
|
||
async function getSmallestFile(directoryPath) { | ||
const fileNames = await fs.promises.readdir(directoryPath); | ||
let smallestFile = null; | ||
let smallestSize = Number.MAX_SAFE_INTEGER; | ||
|
||
for (const fileName of fileNames) { | ||
const filePath = path.join(directoryPath, fileName); | ||
const stats = await fs.promises.stat(filePath); | ||
|
||
if (stats.size < smallestSize) { | ||
smallestFile = filePath; | ||
smallestSize = stats.size; | ||
} | ||
} | ||
|
||
return smallestFile; | ||
} | ||
|
||
function getContractFileName(config, address) { | ||
for (const [key, currentValue] of Object.entries(config)) { | ||
if (currentValue === address) { | ||
return key.charAt(0).toUpperCase() + key.slice(1); | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
async function findFilePath(startPath, targetFileName) { | ||
if (!fs.existsSync(startPath)) { | ||
throw new Error(`Start path does not exist: ${startPath}`); | ||
} | ||
|
||
const directoriesToSearch = [startPath]; | ||
|
||
while (directoriesToSearch.length > 0) { | ||
const currentDirectory = directoriesToSearch.pop(); | ||
const filesAndDirectories = fs.readdirSync(currentDirectory, { withFileTypes: true }); | ||
|
||
for (const fileOrDirectory of filesAndDirectories) { | ||
const fullPath = path.join(currentDirectory, fileOrDirectory.name); | ||
|
||
if (fileOrDirectory.isDirectory()) { | ||
directoriesToSearch.push(fullPath); | ||
} else if (fileOrDirectory.isFile() && fileOrDirectory.name === targetFileName) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: if we convert |
||
return fullPath; | ||
} | ||
} | ||
} | ||
|
||
throw new Error(`File not found: ${targetFileName}`); | ||
} | ||
|
||
async function verifyFilfox(env, contract, options) { | ||
const { dir, contractName } = options; | ||
|
||
if (!dir || !contractName) { | ||
throw new Error('Invalid verification options'); | ||
} | ||
|
||
const config = require(`../../info/${env}.json`); | ||
const contractConfig = config.chains.filecoin.contracts[contractName]; | ||
const contractFileName = getContractFileName(contractConfig, contract); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't contractName already being passed in? you can enforce it to be passed in, and update usages in our code instead of the limited reverse map There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The config map has different names for addresses than actual contract file names, so this should fail @deanamiel. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @milapsheth , @deanamiel can we modify all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
|
||
const contractPath = await findFilePath(dir, `${contractFileName}.sol`); | ||
|
||
const sourceFiles = { | ||
[`${contractFileName}.sol`]: { | ||
content: fs.readFileSync(contractPath, 'utf8'), | ||
}, | ||
}; | ||
|
||
let buildInfo; | ||
|
||
try { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move try/catch to be specifically over the code that can fail |
||
const buildInfoPath = path.join(dir, 'build-info'); | ||
const smallestFile = await getSmallestFile(buildInfoPath); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the logic behind getting the smallest one? non-obvious behaviour should be commented There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that there are often multiple build files produced within the |
||
|
||
if (!smallestFile) { | ||
throw new Error('No build info files found'); | ||
} | ||
|
||
const buildInfoContent = fs.readFileSync(smallestFile, 'utf8'); | ||
buildInfo = JSON.parse(buildInfoContent); | ||
} catch (error) { | ||
console.error('Error reading contract build info', error); | ||
} | ||
|
||
const language = buildInfo.input?.language; | ||
const compiler = buildInfo.solcLongVersion; | ||
const settings = buildInfo.input?.settings; | ||
|
||
const optimize = settings.optimizer?.enabled || true; | ||
const optimizeRuns = settings.optimizer?.runs; | ||
const optimizerDetails = settings.optimizer?.details || ''; | ||
const evmVersion = settings.evmVersion; | ||
|
||
const data = { | ||
address: contract, | ||
language, | ||
compiler, | ||
optimize, | ||
optimizeRuns, | ||
optimizerDetails, | ||
sourceFiles, | ||
license: 'MIT License (MIT)', | ||
evmVersion, | ||
viaIR: false, | ||
libraries: '', | ||
metadata: '', | ||
}; | ||
|
||
const api = config.chains.filecoin.explorer?.api; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. get the API from hardhat network or options.api? |
||
|
||
if (!api) { | ||
throw new Error(`Explorer API not present for filecoin ${env}`); | ||
} | ||
|
||
try { | ||
const response = await axios.post(api, data, { | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
}); | ||
|
||
console.log('Verification response:', JSON.stringify(response.data)); | ||
} catch (error) { | ||
console.error('Error during verification:', JSON.stringify(error.response.data)); | ||
} | ||
} | ||
|
||
/** | ||
* Verifies a contract on etherscan-like explorer of the provided chain using hardhat. | ||
|
@@ -12,7 +146,12 @@ const { writeFileSync } = require('fs'); | |
* @param {any[]} args | ||
* @returns {void} | ||
*/ | ||
const verifyContract = (env, chain, contract, args, options = {}) => { | ||
const verifyContract = async (env, chain, contract, args, options = {}) => { | ||
if (chain.toLowerCase() === 'filecoin') { | ||
await verifyFilfox(env, contract, options); | ||
return; | ||
} | ||
|
||
const stringArgs = args.map((arg) => JSON.stringify(arg)); | ||
const content = `module.exports = [\n ${stringArgs.join(',\n ')}\n];`; | ||
const file = 'temp-arguments.js'; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these aren't dev dependencies since it's for an exported function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I didn't have them as dependencies axelar chains config tests were failing, should I move them from dev dependencies into normal dependencies?