-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5507 from rimrakhimov/rimrakhimov/blockscout-plugin
Feature: blockscout plugin support
- Loading branch information
Showing
14 changed files
with
491 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@nomicfoundation/hardhat-verify": patch | ||
--- | ||
|
||
Added Blockscout as a verification provider |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Blockscout } from "./internal/blockscout"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
packages/hardhat-verify/src/internal/blockscout.chain-config.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import type { ChainConfig } from "../types"; | ||
|
||
// See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#list-of-chain-ids | ||
export const builtinChains: ChainConfig[] = [ | ||
{ | ||
network: "mainnet", | ||
chainId: 1, | ||
urls: { | ||
apiURL: "https://eth.blockscout.com/api", | ||
browserURL: "https://eth.blockscout.com/", | ||
}, | ||
}, | ||
{ | ||
network: "sepolia", | ||
chainId: 11155111, | ||
urls: { | ||
apiURL: "https://eth-sepolia.blockscout.com/api", | ||
browserURL: "https://eth-sepolia.blockscout.com/", | ||
}, | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import type { EthereumProvider } from "hardhat/types"; | ||
import type { ChainConfig } from "../types"; | ||
|
||
import { HARDHAT_NETWORK_NAME } from "hardhat/plugins"; | ||
|
||
import { | ||
ChainConfigNotFoundError, | ||
HardhatNetworkNotSupportedError, | ||
} from "./errors"; | ||
import { ValidationResponse } from "./utilities"; | ||
import { builtinChains } from "./blockscout.chain-config"; | ||
|
||
import { Etherscan } from "./etherscan"; | ||
|
||
/** | ||
* Blockscout verification provider for verifying smart contracts. | ||
*/ | ||
export class Blockscout { | ||
private _etherscan: Etherscan; | ||
|
||
/** | ||
* Create a new instance of the Blockscout verification provider. | ||
* @param apiUrl - The Blockscout API URL, e.g. https://eth.blockscout.com/api. | ||
* @param browserUrl - The Blockscout browser URL, e.g. https://eth.blockscout.com. | ||
*/ | ||
constructor(public apiUrl: string, public browserUrl: string) { | ||
this._etherscan = new Etherscan("api_key", apiUrl, browserUrl); | ||
} | ||
|
||
public static async getCurrentChainConfig( | ||
networkName: string, | ||
ethereumProvider: EthereumProvider, | ||
customChains: ChainConfig[] | ||
): Promise<ChainConfig> { | ||
const currentChainId = parseInt( | ||
await ethereumProvider.send("eth_chainId"), | ||
16 | ||
); | ||
|
||
const currentChainConfig = [ | ||
// custom chains has higher precedence than builtin chains | ||
...[...customChains].reverse(), // the last entry has higher precedence | ||
...builtinChains, | ||
].find(({ chainId }) => chainId === currentChainId); | ||
|
||
if (currentChainConfig === undefined) { | ||
if (networkName === HARDHAT_NETWORK_NAME) { | ||
throw new HardhatNetworkNotSupportedError(); | ||
} | ||
|
||
throw new ChainConfigNotFoundError(currentChainId); | ||
} | ||
|
||
return currentChainConfig; | ||
} | ||
|
||
public static fromChainConfig(chainConfig: ChainConfig): Blockscout { | ||
const apiUrl = chainConfig.urls.apiURL; | ||
const browserUrl = chainConfig.urls.browserURL.trim().replace(/\/$/, ""); | ||
|
||
return new Blockscout(apiUrl, browserUrl); | ||
} | ||
|
||
/** | ||
* Check if a smart contract is verified on Blockscout. | ||
* @link https://docs.blockscout.com/for-users/api/rpc-endpoints/contract#get-contract-source-code-for-a-verified-contract | ||
* @param address - The address of the smart contract. | ||
* @returns True if the contract is verified, false otherwise. | ||
* @throws {NetworkRequestError} if there is an error on the request. | ||
* @throws {ContractVerificationInvalidStatusCodeError} if the API returns an invalid status code. | ||
*/ | ||
public async isVerified(address: string): Promise<boolean> { | ||
return this._etherscan.isVerified(address); | ||
} | ||
|
||
/** | ||
* Verify a smart contract on Blockscout. | ||
* @link https://docs.blockscout.com/for-users/api/rpc-endpoints/contract#verify-a-contract-with-standard-input-json-file | ||
* @param contractAddress - The address of the smart contract to verify. | ||
* @param sourceCode - The source code of the smart contract. | ||
* @param contractName - The name of the smart contract, e.g. "contracts/Sample.sol:MyContract" | ||
* @param compilerVersion - The version of the Solidity compiler used, e.g. `v0.8.19+commit.7dd6d404` | ||
* @returns A promise that resolves to an `BlockscoutResponse` object. | ||
* @throws {NetworkRequestError} if there is an error on the request. | ||
* @throws {ContractVerificationInvalidStatusCodeError} if the API returns an invalid status code. | ||
* @throws {ContractVerificationMissingBytecodeError} if the bytecode is not found on the block explorer. | ||
* @throws {ContractAlreadyVerifiedError} if the contract is already verified. | ||
* @throws {HardhatVerifyError} if the response status is not OK. | ||
*/ | ||
public async verify( | ||
contractAddress: string, | ||
sourceCode: string, | ||
contractName: string, | ||
compilerVersion: string | ||
): Promise<BlockscoutResponse> { | ||
const etherscanResponse = await this._etherscan.verify( | ||
contractAddress, | ||
sourceCode, | ||
contractName, | ||
compilerVersion, | ||
"" | ||
); | ||
|
||
return new BlockscoutResponse( | ||
etherscanResponse.status, | ||
etherscanResponse.message | ||
); | ||
} | ||
|
||
/** | ||
* Get the verification status of a smart contract from Blockscout. | ||
* This method performs polling of the verification status if it's pending. | ||
* @link https://docs.blockscout.com/for-users/api/rpc-endpoints/contract#return-status-of-a-verification-attempt | ||
* @param guid - The verification GUID to check. | ||
* @returns A promise that resolves to an `BlockscoutResponse` object. | ||
* @throws {NetworkRequestError} if there is an error on the request. | ||
* @throws {ContractStatusPollingInvalidStatusCodeError} if the API returns an invalid status code. | ||
* @throws {ContractStatusPollingResponseNotOkError} if the response status is not OK. | ||
*/ | ||
public async getVerificationStatus( | ||
guid: string | ||
): Promise<BlockscoutResponse> { | ||
const etherscanResponse = await this._etherscan.getVerificationStatus(guid); | ||
|
||
return new BlockscoutResponse( | ||
etherscanResponse.status, | ||
etherscanResponse.message | ||
); | ||
} | ||
|
||
/** | ||
* Get the Blockscout URL for viewing a contract's details. | ||
* @param address - The address of the smart contract. | ||
* @returns The URL to view the contract on Blockscout's website. | ||
*/ | ||
public getContractUrl(address: string): string { | ||
return `${this.browserUrl}/address/${address}#code`; | ||
} | ||
} | ||
|
||
class BlockscoutResponse implements ValidationResponse { | ||
public readonly status: number; | ||
public readonly message: string; | ||
|
||
constructor(status: number, message: string) { | ||
this.status = status; | ||
this.message = message; | ||
} | ||
|
||
public isPending() { | ||
return this.message === "Pending in queue"; | ||
} | ||
|
||
public isFailure() { | ||
return this.message === "Fail - Unable to verify"; | ||
} | ||
|
||
public isSuccess() { | ||
return this.message === "Pass - Verified"; | ||
} | ||
|
||
public isAlreadyVerified() { | ||
return ( | ||
// returned by blockscout | ||
this.message.startsWith("Smart-contract already verified") || | ||
// returned by etherscan | ||
this.message.startsWith("Contract source code already verified") || | ||
this.message.startsWith("Already Verified") | ||
); | ||
} | ||
|
||
public isOk() { | ||
return this.status === 1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.