|
|
@ -5,203 +5,202 @@ import axios from 'axios' |
|
|
|
import { PluginClient } from "@remixproject/plugin" |
|
|
|
import { PluginClient } from "@remixproject/plugin" |
|
|
|
|
|
|
|
|
|
|
|
const resetAfter10Seconds = (client: PluginClient, setResults: (value: string) => void) => { |
|
|
|
const resetAfter10Seconds = (client: PluginClient, setResults: (value: string) => void) => { |
|
|
|
setTimeout(() => { |
|
|
|
setTimeout(() => { |
|
|
|
client.emit("statusChanged", { key: "none" }) |
|
|
|
client.emit("statusChanged", { key: "none" }) |
|
|
|
setResults("") |
|
|
|
setResults("") |
|
|
|
}, 10000) |
|
|
|
}, 10000) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export type EtherScanReturn = { |
|
|
|
export type EtherScanReturn = { |
|
|
|
guid: any, |
|
|
|
guid: any, |
|
|
|
status: any, |
|
|
|
status: any, |
|
|
|
} |
|
|
|
} |
|
|
|
export const verify = async ( |
|
|
|
export const verify = async ( |
|
|
|
apiKeyParam: string, |
|
|
|
apiKeyParam: string, |
|
|
|
contractAddress: string, |
|
|
|
contractAddress: string, |
|
|
|
contractArgumentsParam: string, |
|
|
|
contractArgumentsParam: string, |
|
|
|
contractName: string, |
|
|
|
contractName: string, |
|
|
|
compilationResultParam: CompilerAbstract, |
|
|
|
compilationResultParam: CompilerAbstract, |
|
|
|
chainRef: number | string, |
|
|
|
chainRef: number | string, |
|
|
|
isProxyContract: boolean, |
|
|
|
isProxyContract: boolean, |
|
|
|
expectedImplAddress: string,
|
|
|
|
expectedImplAddress: string,
|
|
|
|
client: PluginClient, |
|
|
|
client: PluginClient, |
|
|
|
onVerifiedContract: (value: EtherScanReturn) => void, |
|
|
|
onVerifiedContract: (value: EtherScanReturn) => void, |
|
|
|
setResults: (value: string) => void |
|
|
|
setResults: (value: string) => void |
|
|
|
) => { |
|
|
|
) => { |
|
|
|
let networkChainId |
|
|
|
let networkChainId |
|
|
|
let etherscanApi |
|
|
|
let etherscanApi |
|
|
|
if (chainRef) { |
|
|
|
if (chainRef) { |
|
|
|
if (typeof chainRef === 'number') { |
|
|
|
if (typeof chainRef === 'number') { |
|
|
|
networkChainId = chainRef |
|
|
|
networkChainId = chainRef |
|
|
|
etherscanApi = getEtherScanApi(networkChainId) |
|
|
|
etherscanApi = getEtherScanApi(networkChainId) |
|
|
|
} else if (typeof chainRef === 'string') etherscanApi = chainRef |
|
|
|
} else if (typeof chainRef === 'string') etherscanApi = chainRef |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
const { network, networkId } = await getNetworkName(client) |
|
|
|
const { network, networkId } = await getNetworkName(client) |
|
|
|
if (network === "vm") { |
|
|
|
if (network === "vm") { |
|
|
|
return { |
|
|
|
return { |
|
|
|
succeed: false, |
|
|
|
succeed: false, |
|
|
|
message: "Cannot verify in the selected network" |
|
|
|
message: "Cannot verify in the selected network" |
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
networkChainId = networkId |
|
|
|
|
|
|
|
etherscanApi = getEtherScanApi(networkChainId) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
networkChainId = networkId |
|
|
|
|
|
|
|
etherscanApi = getEtherScanApi(networkChainId) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
const contractMetadata = getContractMetadata( |
|
|
|
|
|
|
|
// cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository
|
|
|
|
|
|
|
|
compilationResultParam.data as unknown as CompilationResult, |
|
|
|
|
|
|
|
contractName |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
if (!contractMetadata) { |
|
|
|
const contractMetadata = getContractMetadata( |
|
|
|
return { |
|
|
|
// cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository
|
|
|
|
succeed: false, |
|
|
|
compilationResultParam.data as unknown as CompilationResult, |
|
|
|
message: "Please recompile contract" |
|
|
|
contractName |
|
|
|
} |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!contractMetadata) { |
|
|
|
const contractMetadataParsed = JSON.parse(contractMetadata) |
|
|
|
return { |
|
|
|
|
|
|
|
succeed: false, |
|
|
|
const fileName = getContractFileName( |
|
|
|
message: "Please recompile contract" |
|
|
|
// cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository
|
|
|
|
|
|
|
|
compilationResultParam.data as unknown as CompilationResult, |
|
|
|
|
|
|
|
contractName |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const jsonInput = { |
|
|
|
|
|
|
|
language: 'Solidity', |
|
|
|
|
|
|
|
sources: compilationResultParam.source.sources, |
|
|
|
|
|
|
|
settings: { |
|
|
|
|
|
|
|
optimizer: { |
|
|
|
|
|
|
|
enabled: contractMetadataParsed.settings.optimizer.enabled, |
|
|
|
|
|
|
|
runs: contractMetadataParsed.settings.optimizer.runs |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const contractMetadataParsed = JSON.parse(contractMetadata) |
|
|
|
const data: { [key: string]: string | any } = { |
|
|
|
|
|
|
|
apikey: apiKeyParam, // A valid API-Key is required
|
|
|
|
|
|
|
|
module: "contract", // Do not change
|
|
|
|
|
|
|
|
action: "verifysourcecode", // Do not change
|
|
|
|
|
|
|
|
codeformat: "solidity-standard-json-input", |
|
|
|
|
|
|
|
sourceCode: JSON.stringify(jsonInput), |
|
|
|
|
|
|
|
contractname: fileName + ':' + contractName, |
|
|
|
|
|
|
|
compilerversion: `v${contractMetadataParsed.compiler.version}`, // see http://etherscan.io/solcversions for list of support versions
|
|
|
|
|
|
|
|
constructorArguements: contractArgumentsParam ? contractArgumentsParam.replace('0x', '') : '', // if applicable
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const fileName = getContractFileName( |
|
|
|
if (isProxyContract) { |
|
|
|
// cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository
|
|
|
|
data.action = "verifyproxycontract" |
|
|
|
compilationResultParam.data as unknown as CompilationResult, |
|
|
|
data.expectedimplementation = expectedImplAddress |
|
|
|
contractName |
|
|
|
data.address = contractAddress |
|
|
|
) |
|
|
|
} else { |
|
|
|
|
|
|
|
data.contractaddress = contractAddress |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const jsonInput = { |
|
|
|
const body = new FormData() |
|
|
|
language: 'Solidity', |
|
|
|
Object.keys(data).forEach((key) => body.append(key, data[key])) |
|
|
|
sources: compilationResultParam.source.sources, |
|
|
|
|
|
|
|
settings: { |
|
|
|
|
|
|
|
optimizer: { |
|
|
|
|
|
|
|
enabled: contractMetadataParsed.settings.optimizer.enabled, |
|
|
|
|
|
|
|
runs: contractMetadataParsed.settings.optimizer.runs |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const data: { [key: string]: string | any } = { |
|
|
|
client.emit("statusChanged", { |
|
|
|
apikey: apiKeyParam, // A valid API-Key is required
|
|
|
|
key: "loading", |
|
|
|
module: "contract", // Do not change
|
|
|
|
type: "info", |
|
|
|
action: "verifysourcecode", // Do not change
|
|
|
|
title: "Verifying ...", |
|
|
|
codeformat: "solidity-standard-json-input", |
|
|
|
}) |
|
|
|
sourceCode: JSON.stringify(jsonInput), |
|
|
|
const response = await axios.post(etherscanApi, body) |
|
|
|
contractname: fileName + ':' + contractName, |
|
|
|
const { message, result, status } = await response.data |
|
|
|
compilerversion: `v${contractMetadataParsed.compiler.version}`, // see http://etherscan.io/solcversions for list of support versions
|
|
|
|
|
|
|
|
constructorArguements: contractArgumentsParam ? contractArgumentsParam.replace('0x', '') : '', // if applicable
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (message === "OK" && status === "1") { |
|
|
|
|
|
|
|
resetAfter10Seconds(client, setResults) |
|
|
|
|
|
|
|
let receiptStatus |
|
|
|
if (isProxyContract) { |
|
|
|
if (isProxyContract) { |
|
|
|
data.action = "verifyproxycontract" |
|
|
|
receiptStatus = await getProxyContractReceiptStatus( |
|
|
|
data.expectedimplementation = expectedImplAddress |
|
|
|
|
|
|
|
data.address = contractAddress |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
data.contractaddress = contractAddress |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const body = new FormData() |
|
|
|
|
|
|
|
Object.keys(data).forEach((key) => body.append(key, data[key])) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client.emit("statusChanged", { |
|
|
|
|
|
|
|
key: "loading", |
|
|
|
|
|
|
|
type: "info", |
|
|
|
|
|
|
|
title: "Verifying ...", |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
const response = await axios.post(etherscanApi, body) |
|
|
|
|
|
|
|
const { message, result, status } = await response.data |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (message === "OK" && status === "1") { |
|
|
|
|
|
|
|
resetAfter10Seconds(client, setResults) |
|
|
|
|
|
|
|
let receiptStatus |
|
|
|
|
|
|
|
if (isProxyContract) { |
|
|
|
|
|
|
|
receiptStatus = await getProxyContractReceiptStatus( |
|
|
|
|
|
|
|
result, |
|
|
|
|
|
|
|
apiKeyParam, |
|
|
|
|
|
|
|
etherscanApi |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
if (receiptStatus.status === '1') { |
|
|
|
|
|
|
|
receiptStatus.message = receiptStatus.result |
|
|
|
|
|
|
|
receiptStatus.result = 'Successfully Updated' |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else receiptStatus = await getReceiptStatus( |
|
|
|
|
|
|
|
result, |
|
|
|
result, |
|
|
|
apiKeyParam, |
|
|
|
apiKeyParam, |
|
|
|
etherscanApi |
|
|
|
etherscanApi |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
if (receiptStatus.status === '1') { |
|
|
|
const returnValue = { |
|
|
|
receiptStatus.message = receiptStatus.result |
|
|
|
guid: result, |
|
|
|
receiptStatus.result = 'Successfully Updated' |
|
|
|
status: receiptStatus.result, |
|
|
|
|
|
|
|
message: `Verification process started correctly. Receipt GUID ${result}`, |
|
|
|
|
|
|
|
succeed: true, |
|
|
|
|
|
|
|
isProxyContract |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
onVerifiedContract(returnValue) |
|
|
|
|
|
|
|
return returnValue |
|
|
|
|
|
|
|
} else if (message === "NOTOK") { |
|
|
|
|
|
|
|
client.emit("statusChanged", { |
|
|
|
|
|
|
|
key: "failed", |
|
|
|
|
|
|
|
type: "error", |
|
|
|
|
|
|
|
title: result, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
const returnValue = { |
|
|
|
|
|
|
|
message: result, |
|
|
|
|
|
|
|
succeed: false, |
|
|
|
|
|
|
|
isProxyContract |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
resetAfter10Seconds(client, setResults) |
|
|
|
} else receiptStatus = await getReceiptStatus( |
|
|
|
return returnValue |
|
|
|
result, |
|
|
|
} |
|
|
|
apiKeyParam, |
|
|
|
return { |
|
|
|
etherscanApi |
|
|
|
message: 'unknown reason ' + result, |
|
|
|
) |
|
|
|
succeed: false |
|
|
|
|
|
|
|
|
|
|
|
const returnValue = { |
|
|
|
|
|
|
|
guid: result, |
|
|
|
|
|
|
|
status: receiptStatus.result, |
|
|
|
|
|
|
|
message: `Verification process started correctly. Receipt GUID ${result}`, |
|
|
|
|
|
|
|
succeed: true, |
|
|
|
|
|
|
|
isProxyContract |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error: any) { |
|
|
|
onVerifiedContract(returnValue) |
|
|
|
console.error(error) |
|
|
|
return returnValue |
|
|
|
setResults("Something wrong happened, try again") |
|
|
|
} else if (message === "NOTOK") { |
|
|
|
return { |
|
|
|
client.emit("statusChanged", { |
|
|
|
message: error.message, |
|
|
|
key: "failed", |
|
|
|
succeed: false |
|
|
|
type: "error", |
|
|
|
|
|
|
|
title: result, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
const returnValue = { |
|
|
|
|
|
|
|
message: result, |
|
|
|
|
|
|
|
succeed: false, |
|
|
|
|
|
|
|
isProxyContract |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
resetAfter10Seconds(client, setResults) |
|
|
|
|
|
|
|
return returnValue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
message: 'unknown reason ' + result, |
|
|
|
|
|
|
|
succeed: false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (error: any) { |
|
|
|
|
|
|
|
console.error(error) |
|
|
|
|
|
|
|
setResults("Something wrong happened, try again") |
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
message: error.message, |
|
|
|
|
|
|
|
succeed: false |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export const getContractFileName = ( |
|
|
|
export const getContractFileName = ( |
|
|
|
compilationResult: CompilationResult, |
|
|
|
compilationResult: CompilationResult, |
|
|
|
contractName: string |
|
|
|
contractName: string |
|
|
|
) => { |
|
|
|
) => { |
|
|
|
const compiledContracts = compilationResult.contracts |
|
|
|
const compiledContracts = compilationResult.contracts |
|
|
|
let fileName = "" |
|
|
|
let fileName = "" |
|
|
|
|
|
|
|
|
|
|
|
for (const file of Object.keys(compiledContracts)) { |
|
|
|
for (const file of Object.keys(compiledContracts)) { |
|
|
|
for (const contract of Object.keys(compiledContracts[file])) { |
|
|
|
for (const contract of Object.keys(compiledContracts[file])) { |
|
|
|
if (contract === contractName) { |
|
|
|
if (contract === contractName) { |
|
|
|
fileName = file |
|
|
|
fileName = file |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return fileName |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return fileName |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export const getContractMetadata = ( |
|
|
|
export const getContractMetadata = ( |
|
|
|
compilationResult: CompilationResult, |
|
|
|
compilationResult: CompilationResult, |
|
|
|
contractName: string |
|
|
|
contractName: string |
|
|
|
) => { |
|
|
|
) => { |
|
|
|
const compiledContracts = compilationResult.contracts |
|
|
|
const compiledContracts = compilationResult.contracts |
|
|
|
let contractMetadata = "" |
|
|
|
let contractMetadata = "" |
|
|
|
|
|
|
|
|
|
|
|
for (const file of Object.keys(compiledContracts)) { |
|
|
|
for (const file of Object.keys(compiledContracts)) { |
|
|
|
for (const contract of Object.keys(compiledContracts[file])) { |
|
|
|
for (const contract of Object.keys(compiledContracts[file])) { |
|
|
|
if (contract === contractName) { |
|
|
|
if (contract === contractName) { |
|
|
|
contractMetadata = compiledContracts[file][contract].metadata |
|
|
|
contractMetadata = compiledContracts[file][contract].metadata |
|
|
|
if (contractMetadata) { |
|
|
|
if (contractMetadata) { |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return contractMetadata |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return contractMetadata |
|
|
|
|
|
|
|
} |
|
|
|