Skip to content

Commit e9a6e95

Browse files
committed
Fetch contracts from Etherscan and add memfs
0 parents  commit e9a6e95

27 files changed

+5893
-0
lines changed

Diff for: .eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.md

Diff for: .eslintrc.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"plugins": ["no-only-tests", "simple-import-sort"],
5+
"extends": ["typestrict"],
6+
"parserOptions": {
7+
"ecmaVersion": "latest",
8+
"sourceType": "module",
9+
"project": "./tsconfig.json"
10+
},
11+
"rules": {
12+
"no-only-tests/no-only-tests": "error",
13+
14+
"simple-import-sort/imports": "error",
15+
"simple-import-sort/exports": "error",
16+
17+
"@typescript-eslint/no-floating-promises": "warn",
18+
19+
"@typescript-eslint/no-use-before-define": "off",
20+
21+
// TypeScript checks this
22+
"no-invalid-this": "off"
23+
},
24+
"ignorePatterns": ["**/*.d.ts", "**/dist/**"]
25+
}

Diff for: .gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.vsix
2+
**/.vscode-test-web/
3+
**/dist
4+
**/node_modules
5+
**/out
6+
dist
7+
node_modules
8+
out

Diff for: .vscode/extensions.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher"]
3+
}

Diff for: .vscode/launch.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Run Web Extension",
6+
"type": "pwa-extensionHost",
7+
"debugWebWorkerHost": true,
8+
"request": "launch",
9+
"args": [
10+
"--extensionDevelopmentPath=${workspaceFolder}/packages/ethereum-viewer",
11+
"--extensionDevelopmentKind=web"
12+
],
13+
"outFiles": ["${workspaceFolder}/packages/ethereum-viewer/dist/**/*.js"],
14+
"preLaunchTask": "npm: watch"
15+
},
16+
{
17+
"name": "Extension Tests",
18+
"type": "extensionHost",
19+
"debugWebWorkerHost": true,
20+
"request": "launch",
21+
"args": [
22+
"--extensionDevelopmentPath=${workspaceFolder}/packages/ethereum-viewer",
23+
"--extensionDevelopmentKind=web",
24+
"--extensionTestsPath=${workspaceFolder}/packages/ethereum-viewer/dist/test/run-tests"
25+
],
26+
"outFiles": ["${workspaceFolder}/packages/ethereum-viewer/dist/**/*.js"],
27+
"preLaunchTask": "npm: watch"
28+
}
29+
]
30+
}

Diff for: .vscode/settings.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"files.exclude": {
3+
"out": false
4+
},
5+
"search.exclude": {
6+
"out": true
7+
},
8+
"typescript.tsc.autoDetect": "off"
9+
}

Diff for: .vscode/tasks.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"type": "npm",
6+
"script": "build",
7+
"group": {
8+
"kind": "build",
9+
"isDefault": true
10+
},
11+
"problemMatcher": ["$ts-webpack", "$tslint-webpack"]
12+
},
13+
{
14+
"type": "npm",
15+
"script": "watch",
16+
"group": "build",
17+
"isBackground": true,
18+
"problemMatcher": ["$ts-webpack-watch", "$tslint-webpack-watch"]
19+
}
20+
]
21+
}

Diff for: README.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# @dethcrypto/ethereum-viewer
2+
3+
_View source of deployed Ethereum contracts in VSCode._
4+
5+
---
6+
7+
### Contributing
8+
9+
#### Resources
10+
11+
Ethereum Viewer is a VSCode Web Extension using FileSystemProvider API to show sources of deployed Ethereum smart contracts.
12+
The following links might be provide some insight, if you're not familiar with some of the aforementioned terms.
13+
14+
- https://code.visualstudio.com/api/extension-guides/web-extensions
15+
- https://code.visualstudio.com/api/references/vscode-api#FileSystemProvider
16+
- https://github.com/microsoft/vscode-extension-samples/blob/main/fsprovider-sample/README.md

Diff for: package.json

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"license": "MIT",
3+
"scripts": {
4+
"watch": "pnpm watch --filter ./packages",
5+
"build": "pnpm build --filter ./packages",
6+
"lint": "eslint --ext .ts ./packages/*/src/**/*.ts",
7+
"lint:fix": "pnpm lint -- --fix"
8+
},
9+
"devDependencies": {
10+
"@types/mocha": "^9.0.0",
11+
"@types/node": "^16.11.12",
12+
"@types/vscode": "^1.63.0",
13+
"@types/webpack": "^5.28.0",
14+
"@types/webpack-env": "^1.16.2",
15+
"@typescript-eslint/eslint-plugin": "^5.3.0",
16+
"@typescript-eslint/parser": "^5.3.0",
17+
"@vscode/test-web": "^0.0.15",
18+
"assert": "^2.0.0",
19+
"earljs": "^0.1.12",
20+
"eslint": "^7",
21+
"eslint-config-typestrict": "^1.0.2",
22+
"eslint-plugin-import": "^2.25.2",
23+
"eslint-plugin-no-only-tests": "^2.6.0",
24+
"eslint-plugin-simple-import-sort": "^7.0.0",
25+
"eslint-plugin-sonarjs": "^0.10.0",
26+
"eslint-plugin-unused-imports": "^1.1.5",
27+
"mocha": "^9.1.3",
28+
"process": "^0.11.10",
29+
"serve": "^13.0.2",
30+
"ts-loader": "^9.2.5",
31+
"ts-node": "^10.4.0",
32+
"typescript": "^4.5.3",
33+
"webpack": "^5.52.1",
34+
"webpack-cli": "^4.8.0"
35+
}
36+
}

Diff for: packages/ethereum-viewer/.vscodeignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.gitignore
2+
.vscode-test-web/**
3+
.yarnrc
4+
**/*.map
5+
**/*.ts
6+
dist/test/**
7+
node_modules/**
8+
out/**
9+
src/**
10+
tsconfig.json
11+
webpack.config.ts

Diff for: packages/ethereum-viewer/.yarnrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--ignore-engines true

Diff for: packages/ethereum-viewer/certs/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.pem

Diff for: packages/ethereum-viewer/package.json

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "ethereum-viewer",
3+
"license": "MIT",
4+
"version": "0.1.0",
5+
"browser": "./dist/extension.js",
6+
"scripts": {
7+
"vscode:prepublish": "yarn run package-web",
8+
"build": "webpack",
9+
"build:prod": "webpack --mode production --devtool hidden-source-map",
10+
"watch": "webpack --watch",
11+
"pretest": "yarn run build",
12+
"lint": "eslint src --ext ts",
13+
"serve": "serve --cors -l 5000 --ssl-cert ./certs/localhost.pem --ssl-key ./certs/localhost-key.pem",
14+
"run-in-browser": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. .",
15+
"test": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. --extensionTestsPath=dist/web/test/suite/index.js"
16+
},
17+
"dependencies": {
18+
"fast-json-stable-stringify": "^2.1.0",
19+
"p-finally": "^2.0.1",
20+
"path-browserify": "^1.0.1",
21+
"ts-essentials": "^9.0.0"
22+
},
23+
"devDependencies": {
24+
"typescript": "^4.5.3"
25+
},
26+
"engines": {
27+
"vscode": "^1.63.0"
28+
},
29+
"displayName": "Ethereum Viewer",
30+
"description": "View source of deployed Ethereum contracts in VSCode",
31+
"publisher": "hasparus",
32+
"categories": [
33+
"Other"
34+
],
35+
"activationEvents": [
36+
"onCommand:ethereum-viewer.helloWorld"
37+
],
38+
"contributes": {
39+
"commands": [
40+
{
41+
"command": "ethereum-viewer.helloWorld",
42+
"title": "Hello World"
43+
}
44+
]
45+
}
46+
}

Diff for: packages/ethereum-viewer/pnpm-lock.yaml

+46
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: packages/ethereum-viewer/src/etherscan.ts

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import stringify from "fast-json-stable-stringify";
2+
import { assert } from "ts-essentials";
3+
4+
import { fetch } from "./util/fetch";
5+
6+
const ETHERSCAN_API_KEY = "862Y3WJ4JB4B34PZQRFEV3IK6SZ8GNR9N5";
7+
8+
export async function fetchFiles(
9+
network: Network,
10+
contractAddress: string
11+
): Promise<FlatFileSystem> {
12+
const api = `https://api${
13+
network === "mainnet" ? "" : `-${network}`
14+
}.etherscan.io/api`;
15+
16+
const url = `${api}?module=contract&action=getsourcecode&address=${contractAddress}&apikey=${ETHERSCAN_API_KEY}`;
17+
18+
const response = (await fetch(url)) as Etherscan.ContractSourceResponse;
19+
20+
assert(
21+
response.message === "OK",
22+
"Failed to fetch contract source\n" + stringify(response)
23+
);
24+
25+
// @todo no idea why res.result is an array.
26+
const data = response.result[0];
27+
28+
// `.SourceCode` is apparently JSON surrounded with one set of curly braces
29+
let sourceCode = JSON.parse(
30+
data.SourceCode.slice(1, -1)
31+
) as Etherscan.ContractSources;
32+
33+
const results: FlatFileSystem = {
34+
"settings.json": stringify(sourceCode.settings),
35+
};
36+
37+
for (const [path, { content }] of Object.entries(sourceCode.sources)) {
38+
results[path] = content;
39+
}
40+
41+
return results;
42+
}
43+
44+
export interface FlatFileSystem extends Record<FilePath, FileContent> {}
45+
46+
export type FilePath = string & { __brand?: "Path" };
47+
48+
export type FileContent = string & { __brand?: "FileContent" };
49+
50+
export type Network = "mainnet" | "ropsten" | "rinkeby" | "kovan" | "goerli";
51+
52+
declare namespace Etherscan {
53+
interface ContractSourceResponse {
54+
message: string;
55+
result: Etherscan.ContractInfo[];
56+
}
57+
58+
interface ContractInfo {
59+
SourceCode: string;
60+
ABI: string;
61+
ContractName: string;
62+
CompilerVersion: string;
63+
OptimizationUsed: string;
64+
Runs: string;
65+
ConstructorArguments: string;
66+
EVMVersion: string;
67+
Library: string;
68+
LicenseType: string;
69+
Proxy: string;
70+
Implementation: string;
71+
SwarmSource: string;
72+
}
73+
74+
interface ContractSources {
75+
language: string;
76+
settings: {
77+
evmVersion: string;
78+
libraries: string[];
79+
metadata: object;
80+
optimizer: object;
81+
outputSelection: object;
82+
remappings: string[];
83+
};
84+
sources: Record<FilePath, File>;
85+
}
86+
87+
type File = { content: FileContent };
88+
89+
type Abi = object[];
90+
}

0 commit comments

Comments
 (0)