Skip to content

Commit 48de72b

Browse files
authored
Add Etherscan link to status bar (#34)
1 parent 7a1eb22 commit 48de72b

File tree

8 files changed

+119
-27
lines changed

8 files changed

+119
-27
lines changed

packages/ethereum-viewer/src/contributedCommands.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { commands, ExtensionContext, QuickPickItem, window } from "vscode";
33
import { ApiName, explorerApiUrls, networkNames } from "./explorer";
44
import { FileSystem } from "./fs";
55
import { openContractSource } from "./openContractSource";
6+
import { renderStatusBarItems } from "./statusBar";
67
import { unsafeEntries } from "./util/unsafeEntries";
78

89
export function registerContributedCommands(
@@ -49,7 +50,13 @@ export function registerContributedCommands(
4950

5051
const { apiName } = picked;
5152

52-
await openContractSource(context, { fs, address, apiName });
53+
const info = await openContractSource(context, { fs, address, apiName });
54+
55+
renderStatusBarItems({
56+
contractAddress: address,
57+
contractName: info.ContractName || "contract",
58+
apiName,
59+
});
5360
},
5461
};
5562

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function apiUrlToWebsite(url: string) {
2+
// This is a bit of a hack, but they all have the same URL scheme.
3+
return url
4+
.replace("//api.", "//")
5+
.replace("//api-", "//")
6+
.replace(/\/api$/, "");
7+
}

packages/ethereum-viewer/src/explorer/fetchFiles.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { assert, StrictOmit } from "ts-essentials";
44
import { fetch as _fetch } from "../util/fetch";
55
import { prettyStringify } from "../util/stringify";
66
import * as types from "./api-types";
7+
import { apiUrlToWebsite } from "./apiUrlToWebsite";
78
import { fileExtension } from "./fileExtension";
89
import { ApiName, explorerApiKeys, explorerApiUrls } from "./networks";
910

@@ -118,7 +119,11 @@ function prefixFiles(files: FileContents, prefix: string): FileContents {
118119

119120
export interface FetchFilesResult {
120121
files: FileContents;
121-
info: ContractInfo & { implementation?: ContractInfo };
122+
info: ContractInfoWithImplementation;
123+
}
124+
125+
export interface ContractInfoWithImplementation extends ContractInfo {
126+
implementation?: ContractInfo;
122127
}
123128

124129
export interface ContractInfo
@@ -141,11 +146,3 @@ Oops! It seems this contract source code is not verified on ${websiteUrl}.
141146
Take a look at ${websiteUrl}/address/${contractAddress}.
142147
`;
143148
}
144-
145-
function apiUrlToWebsite(url: string) {
146-
// This is a bit of a hack, but they all have the same URL scheme.
147-
return url
148-
.replace("//api.", "//")
149-
.replace("//api-", "//")
150-
.replace(/\/api$/, "");
151-
}

packages/ethereum-viewer/src/extension.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as explorer from "./explorer";
66
import { explorerApiUrls, networkNames } from "./explorer";
77
import { FileSystem } from "./fs";
88
import { openContractSource } from "./openContractSource";
9+
import { renderStatusBarItems } from "./statusBar";
910

1011
let initialized = false;
1112
const fs = FileSystem();
@@ -42,7 +43,23 @@ async function main(context: vscode.ExtensionContext) {
4243
return;
4344
}
4445

45-
await openContractSource(context, { fs, apiName, address });
46+
renderStatusBarItems({
47+
contractAddress: address,
48+
contractName: "contract",
49+
apiName,
50+
});
51+
52+
const info = await openContractSource(context, {
53+
fs,
54+
apiName,
55+
address,
56+
});
57+
58+
renderStatusBarItems({
59+
contractAddress: address,
60+
contractName: info.ContractName || "contract",
61+
apiName,
62+
});
4663
}
4764

4865
async function detectExplorerApiName(): Promise<explorer.ApiName> {

packages/ethereum-viewer/src/openContractSource.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,9 @@ export async function openContractSource(
4949
);
5050
context.subscriptions.push(textSearchProviderDisposable);
5151

52-
// We're trying to open the file even if it doesn't exist yet.
5352
await showTextDocument(mainFile);
54-
// Because the following seems to be very slow:
55-
// // onFileChangeOnce(
56-
// // context,
57-
// // fs,
58-
// // mainFile,
59-
// // (e) => void showTextDocument(e.uri.path)
60-
// // );
61-
// It's causing some errors in the console, but in the end it provides better UX.
53+
54+
return info;
6255
}
6356

6457
async function saveContractFilesToFs({
@@ -106,6 +99,15 @@ function getMainContractFile(
10699
}
107100

108101
async function showTextDocument(path: string) {
102+
// We're trying to open the file even if it doesn't exist yet.
103+
// Because the following seems to be very slow:
104+
// // onFileChangeOnce(
105+
// // context,
106+
// // fs,
107+
// // mainFile,
108+
// // (e) => void showTextDocument(e.uri.path)
109+
// // );
110+
// It's causing some errors in the console, but in the end it provides better UX.
109111
await window.showTextDocument(
110112
Uri.from({ scheme: "memfs", path: "/" + path })
111113
);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { StatusBarAlignment, StatusBarItem, Uri, window } from "vscode";
2+
3+
import { apiUrlToWebsite } from "./explorer/apiUrlToWebsite";
4+
import { ApiName, explorerApiUrls } from "./explorer/networks";
5+
6+
export function renderStatusBarItems(
7+
args:
8+
| {
9+
contractAddress: string;
10+
contractName: string;
11+
apiName: ApiName;
12+
}
13+
| {}
14+
) {
15+
if ("contractAddress" in args) {
16+
const { apiName, contractAddress, contractName } = args;
17+
const website = apiUrlToWebsite(explorerApiUrls[apiName]);
18+
const link = `${website}/address/${contractAddress}`;
19+
20+
const where = apiName.endsWith("etherscan")
21+
? "on Etherscan"
22+
: "in the explorer";
23+
24+
const tooltip = `Open ${link}`;
25+
renderStatusBarItem({
26+
key: "ethereum-viewer.etherscan-link",
27+
text: `$(eye) See ${contractName} ${where} (${contractAddress})`,
28+
tooltip,
29+
command: {
30+
title: "Open on Etherscan",
31+
command: "vscode.open",
32+
arguments: [Uri.parse(link)],
33+
},
34+
priority: 0,
35+
});
36+
}
37+
}
38+
39+
const _renderedItems = new Map<string, StatusBarItem>();
40+
41+
interface RenderStatusBarItemArgs
42+
extends Pick<StatusBarItem, "text" | "tooltip" | "command">,
43+
Pick<window.StatusBarItemOptions, "alignment" | "priority"> {
44+
priority?: number;
45+
key: string;
46+
}
47+
48+
function renderStatusBarItem(args: RenderStatusBarItemArgs) {
49+
let item = _renderedItems.get(args.key)!;
50+
51+
item ||= window.createStatusBarItem(
52+
args.alignment || StatusBarAlignment.Left,
53+
args.priority
54+
);
55+
56+
item.tooltip = args.tooltip;
57+
item.text = args.text;
58+
item.command = args.command;
59+
60+
item.show();
61+
62+
_renderedItems.set(args.key, item);
63+
64+
return item;
65+
}

packages/ethereum-viewer/webpack.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const webExtensionConfig: webpack.Configuration = {
5151
performance: {
5252
hints: false,
5353
},
54-
devtool: "nosources-source-map",
54+
devtool: "eval-cheap-module-source-map",
5555
infrastructureLogging: {
5656
level: "log",
5757
},

packages/vscode-host/src/code/browser/workbench/workbench.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ async function main() {
5353
config = { ...config, workspaceProvider };
5454
}
5555

56-
const contractAddress = ethViewerCommands.getContractAddress();
57-
5856
setTimeout(() => renderNotification(), 500);
5957

58+
const apiName = ethViewerCommands.getApiName() || "etherscan";
59+
6060
create(document.body, {
6161
...config,
6262
commands: getCommands(),
@@ -65,10 +65,7 @@ async function main() {
6565
},
6666
windowIndicator: {
6767
onDidChange: Event.None,
68-
label: localize(
69-
"playgroundLabel",
70-
`$(remote) deth.net` + (contractAddress ? `: ${contractAddress}` : "")
71-
),
68+
label: localize("playgroundLabel", `$(remote) ${apiName}.deth.net`),
7269
tooltip: localize(
7370
"playgroundTooltip",
7471
"See Ethereum Code Viewer on GitHub"

0 commit comments

Comments
 (0)