Skip to content
This repository has been archived by the owner on Oct 23, 2020. It is now read-only.

Commit

Permalink
Merge pull request #2 from NodeFactoryIo/master
Browse files Browse the repository at this point in the history
Plugin with task for verifying contracts on etherscan
  • Loading branch information
alcuadrado authored Apr 6, 2019
2 parents bc798fb + 147b68d commit 802a3c9
Show file tree
Hide file tree
Showing 27 changed files with 4,335 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
TS_NODE_FILES=true

WALLET_PRIVATE_KEY=0xB60B1D45848F32C21EB483EE1F83EFCBE33B896E3F16044821A0DB4BF209FA8B

ETHERSCAN_API_KEY=QJBGHU5IH52MIB2HNYP51ZWIBFR6MYGNY6
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/dist
/test/buidler-project/cache/
# Logs
logs
*.log
Expand Down Expand Up @@ -59,3 +61,7 @@ typings/

# next.js build output
.next

#IDE settings
.idea/
sample
21 changes: 21 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
language: node_js

node_js:
- "8"
- "10"

install:
- yarn global add codecov
- yarn
- yarn add --peer @nomiclabs/buidler@^1.0.0-beta.2

script:
- yarn test:unit
- codecov
- yarn lint

cache: yarn

branches:
only:
- master
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,42 @@
# buidler-etherscan
Buidler plugin for verifying contracts on Etherscan

## Installation

We recommend developing Buidler plugins using yarn. To start working on your project, just run

- `yarn`
- `yarn add --peer @nomiclabs/buidler@^1.0.0-beta.2`

## Updating Buidler or other peer dependencies

When updating/adding Buidler or other peer dependencies, you should update the `.travis.yml` file's install section. The right version of all of them has to be installed in a single line, after `yarn`.

## Testing

Running `yarn test` will run every test located in the `test/` folder. They use [mocha](https://mochajs.org) and [chai](https://www.chaijs.com/), but you can customize them.

You can run only integration tests with `yarn run test:integration` and unit with `yarn run test:unit`.

For integration tests, it is required to set env (.env file) variable `WALLET_PRIVATE_KEY` with ropsten wallet private key.

We recommend creating unit tests for your own modules, and integration tests for the interaction of the plugin with Buidler and its dependencies.

## Linting and autoformat

All all of Buidler projects use [prettier](https://prettier.io/) and [tslint](https://palantir.github.io/tslint/).

You can check if your code style is correct by running `yarn lint`, and fix it with `yarn lint:fix`.

## Building the project

Just run `yarn buidl` ️👷‍

## README file

We recommend writing a README that contains the following information:

* What is it
* How to install it
* New tasks
* Environment extensions
62 changes: 62 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"name": "@nomiclabs/buidler-etherscan",
"version": "0.0.1",
"description": "Buidler TypeScript plugin for verifying contracts on etherscan",
"repository": "github:nomiclabs/buidler-etherscan",
"contributors": [
"Nomic Labs SRL",
"Marin Petrunić <[email protected]>"
],
"license": "MIT",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"scripts": {
"lint:fix": "prettier --write 'src/**/*.{js,ts}' 'test/**/*.{ts}' && tslint --fix --config tslint.json --project tsconfig.json",
"lint": "tslint --config tslint.json --project tsconfig.json",
"test": "yarn run test:unit && yarn run test:integration",
"test:unit": "mocha --recursive \"test/unit/**/*.ts\"",
"test:integration": "mocha --recursive \"test/integration/**/*.ts\"",
"build": "tsc",
"buidl": "tsc",
"watch": "tsc -w"
},
"files": [
"dist/src/",
"src/",
"LICENSE",
"README.md"
],
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/download": "^6.2.4",
"@types/ethereumjs-abi": "^0.6.3",
"@types/find-up": "^2.1.1",
"@types/fs-extra": "^5.0.4",
"@types/mocha": "^5.2.5",
"@types/nock": "^9.3.1",
"@types/node": "^8.10.38",
"@types/request": "^2.48.1",
"@types/request-promise": "^4.1.42",
"chai": "^4.2.0",
"dotenv": "^6.2.0",
"ethers": "^4.0.27",
"mocha": "^5.2.0",
"nock": "^10.0.6",
"prettier": "^1.15.3",
"solc": "^0.5.6",
"source-map-support": "^0.5.9",
"ts-node": "^8.0.2",
"tslint": "^5.12.0",
"tslint-config-prettier": "^1.17.0",
"tslint-plugin-prettier": "^2.0.1",
"typescript": "^3.2.2"
},
"peerDependencies": {
"@nomiclabs/buidler": "1.0.0-beta.2"
},
"dependencies": {
"ethereumjs-abi": "^0.6.7",
"request": "^2.88.0",
"request-promise": "^4.2.4"
}
}
26 changes: 26 additions & 0 deletions src/AbiEncoder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { BuidlerPluginError } from "@nomiclabs/buidler/plugins";
import abi from "ethereumjs-abi";

export default class AbiEncoder {
public static encodeConstructor(
contractAbi: any[],
constructorArguments: string[]
): string {
let constructorAbis: any[] = contractAbi.filter(
value => value.type === "constructor"
);
if (constructorAbis.length === 0) {
return "";
}
constructorAbis = constructorAbis.filter(
value => value.inputs.length === constructorArguments.length
);
if (constructorAbis.length === 0) {
throw new BuidlerPluginError("Invalid number of constructor arguments");
}
const types = constructorAbis[0].inputs.map(
(value: { type: string }) => value.type
);
return abi.rawEncode(types, constructorArguments).toString("hex");
}
}
56 changes: 56 additions & 0 deletions src/ContractCompiler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { TASK_COMPILE_RUN_COMPILER } from "@nomiclabs/buidler/builtin-tasks/task-names";
import { BuidlerPluginError } from "@nomiclabs/buidler/plugins";
import { RunTaskFunction } from "@nomiclabs/buidler/types";

export default class ContractCompiler {
constructor(private readonly runTask: RunTaskFunction) {}

public async getAbi(source: string, contractName: string): Promise<any> {
return (await this.compile(source, contractName)).abi;
}

public async compile(
source: string,
contractName: string
): Promise<{ abi: any[]; bytecode: string }> {
const compilationResult = await this.runTask(TASK_COMPILE_RUN_COMPILER, {
input: this.getSolcInput(source)
});
if (compilationResult.errors) {
throw new BuidlerPluginError(
"Failed to compile: " + JSON.stringify(compilationResult.errors)
);
}
const contracts: any = compilationResult.contracts.contracts;
for (const contract in contracts) {
if (contracts.hasOwnProperty(contract) && contract === contractName) {
return {
abi: contracts[contract].abi,
bytecode: contracts[contract].evm.bytecode.object
};
}
}
throw new BuidlerPluginError(
`Given contract name (${contractName}) doesn't exist in sources`
);
}

private getSolcInput(source: string) {
return {
language: "Solidity",
sources: {
contracts: {
content: source
}
},

settings: {
outputSelection: {
"*": {
"*": ["*"]
}
}
}
};
}
}
18 changes: 18 additions & 0 deletions src/etherscan/EtherscanResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default class EtherscanResponse {
public readonly status: number;

public readonly message: string;

public constructor(response: any) {
this.status = parseInt(response.status, 10);
this.message = response.result;
}

public isPending() {
return this.message === "Pending in queue";
}

public isOk() {
return this.status === 1;
}
}
62 changes: 62 additions & 0 deletions src/etherscan/EtherscanService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { BuidlerPluginError } from "@nomiclabs/buidler/plugins";
import request from "request-promise";

import EtherscanResponse from "./EtherscanResponse";
import EtherscanVerifyContractRequest from "./EtherscanVerifyContractRequest";

export default class EtherscanService {
constructor(private readonly url: string) {}

public async verifyContract(
req: EtherscanVerifyContractRequest
): Promise<EtherscanResponse> {
try {
const response = new EtherscanResponse(
await request.post(this.url, { form: req, json: true })
);

if (!response.isOk()) {
throw new BuidlerPluginError(response.message);
}
return response;
} catch (e) {
throw new BuidlerPluginError(
"Failed to send contract verification request. Reason: " + e.message,
e
);
}
}

public async getVerificationStatus(guid: string): Promise<EtherscanResponse> {
try {
const response = new EtherscanResponse(
await request.get(this.url, {
json: true,
qs: {
module: "contract",
action: "checkverifystatus",
guid
}
})
);
if (response.isPending()) {
await this.delay(2000);
return this.getVerificationStatus(guid);
}
if (!response.isOk()) {
throw new BuidlerPluginError(response.message);
}
return response;
} catch (e) {
throw new BuidlerPluginError(
"Failed to verify contract. Reason: " + e.message
);
}
}

private delay(ms: number): Promise<void> {
return new Promise(function(resolve) {
setTimeout(resolve, ms);
});
}
}
67 changes: 67 additions & 0 deletions src/etherscan/EtherscanVerifyContractRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { BuidlerPluginError } from "@nomiclabs/buidler/plugins";
import { SolcConfig } from "@nomiclabs/buidler/types";

import { EtherscanBuidlerEnvironment } from "../index";

export default class EtherscanVerifyContractRequest {
// for that weird etherscan library props
[key: string]: any;
public readonly apikey: string;
public readonly module: string = "contract";
public readonly action: string = "verifysourcecode";
public readonly contractaddress: string;
public readonly sourceCode: string;
public readonly contractname: string;
public readonly compilerversion: string;
public readonly optimizationUsed: number;
public readonly runs: number;
public readonly constructorArguements: string;

constructor(
etherscanConfig: EtherscanBuidlerEnvironment,
solcConfig: SolcConfig,
contractName: string,
address: string,
libraries: string,
source: string,
constructorArguments: string
) {
this.apikey = etherscanConfig.apiKey;
this.contractaddress = address;
this.sourceCode = source;
this.contractname = contractName;
this.compilerversion = solcConfig.fullVersion;
this.optimizationUsed = solcConfig.optimizer.enabled ? 1 : 0;
this.runs = solcConfig.optimizer.runs;
this.constructorArguements = constructorArguments;
this.setLibraries(libraries);
}

public serialize(): string {
return JSON.stringify(this);
}

private setLibraries(libraries: string) {
let i: number = 1;
let parsedLibraries: { [key: string]: string } = {};
try {
if (libraries) {
parsedLibraries = JSON.parse(libraries);
}
} catch (e) {
throw new BuidlerPluginError(
"Failed to parse libraries. Reason: " + e.message
);
}
for (const libraryName in parsedLibraries) {
if (parsedLibraries.hasOwnProperty(libraryName)) {
this["libraryname" + i] = libraryName;
this["libraryaddress" + i] = parsedLibraries[libraryName];
i++;
if (i >= 10) {
break;
}
}
}
}
}
Loading

0 comments on commit 802a3c9

Please sign in to comment.