Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 9e51fe6

Browse files
authored
Merge pull request #4346 from trufflesuite/extract-web3-eth
Internal improvement: Remove web3-eth from decoder
2 parents fe15aae + 9500eea commit 9e51fe6

File tree

5 files changed

+216
-30
lines changed

5 files changed

+216
-30
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import type { BlockSpecifier, RegularizedBlockSpecifier } from "./types";
2+
import type BN from "bn.js";
3+
import type { Provider } from "web3/providers";
4+
import { promisify } from "util";
5+
6+
// lifted from @types/web3
7+
type Log = {
8+
address: string;
9+
data: string;
10+
topics: string[];
11+
logIndex: number;
12+
transactionHash: string;
13+
transactionIndex: number;
14+
blockHash: string;
15+
blockNumber: number;
16+
}
17+
type PastLogsOptions = {
18+
toBlock?: string | number;
19+
fromBlock?: string | number;
20+
address?: string | string[];
21+
};
22+
type SendRequestArgs = {
23+
method: string;
24+
params: unknown[];
25+
};
26+
type Eip1193Provider = {
27+
request: (options: { method: string; params?: unknown[] | object; }) => Promise<any>;
28+
}
29+
type Block = {
30+
number: string;
31+
hash: string;
32+
parentHash: string;
33+
mixHash: string;
34+
nonce: string;
35+
sha3Uncles: string;
36+
logsBloom: string;
37+
transactionsRoot: string;
38+
stateRoot: string;
39+
receiptsRoot: string;
40+
miner: string;
41+
difficulty: string;
42+
totalDifficulty: string;
43+
extraData: string;
44+
size: string;
45+
gasLimit: string;
46+
gasUsed: string;
47+
timestamp: string;
48+
transactions: string[];
49+
uncles: string[];
50+
}
51+
const stringWhitelist = [
52+
"latest",
53+
"pending",
54+
"genesis",
55+
"earliest"
56+
];
57+
58+
const formatBlockSpecifier = (block: BlockSpecifier): string => {
59+
if (typeof block === "string" && stringWhitelist.includes(block)) {
60+
// block is one of 'latest', 'pending', 'earliest', or 'genesis'
61+
return block === "genesis" ?
62+
// convert old web3 input format which uses 'genesis'
63+
"earliest" :
64+
block;
65+
} else if (typeof block === "string" && !isNaN(parseInt(block))) {
66+
// block is a string representation of a number
67+
if (block.startsWith("0x")) return block;
68+
// convert to hex and add '0x' prefix in case block is decimal
69+
return `0x${parseInt(block).toString(16)}`;
70+
} else if (typeof block === "number") {
71+
return `0x${block.toString(16)}`;
72+
} else {
73+
throw new Error(
74+
"The block specified must be a number or one of the strings 'latest'," +
75+
"'pending', or 'earliest'."
76+
);
77+
}
78+
};
79+
80+
export class ProviderAdapter {
81+
public provider: Provider | Eip1193Provider;
82+
83+
constructor (provider: Provider | Eip1193Provider) {
84+
this.provider = provider;
85+
}
86+
87+
private async sendRequest ({
88+
method,
89+
params
90+
}: SendRequestArgs): Promise<any> {
91+
if (!this.provider) {
92+
throw new Error("There is not a valid provider present.")
93+
}
94+
// check to see if the provider is compliant with eip1193
95+
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md
96+
if ("request" in this.provider) {
97+
return (await this.provider.request({ method, params })).result;
98+
} else {
99+
const sendMethod = promisify(this.provider.send).bind(this.provider);
100+
return (await sendMethod({
101+
jsonrpc: "2.0",
102+
id: new Date().getTime(),
103+
method,
104+
params
105+
})).result;
106+
}
107+
}
108+
109+
public async getCode (address: string, block: RegularizedBlockSpecifier): Promise<string> {
110+
const blockToFetch = formatBlockSpecifier(block);
111+
return await this.sendRequest({
112+
method: "eth_getCode",
113+
params: [
114+
address,
115+
blockToFetch
116+
]
117+
});
118+
}
119+
120+
public async getBlockByNumber (block: BlockSpecifier): Promise<Block> {
121+
const blockToFetch = formatBlockSpecifier(block);
122+
return await this.sendRequest({
123+
method: "eth_getBlockByNumber",
124+
params: [ blockToFetch, false ]
125+
});
126+
}
127+
128+
public async getPastLogs ({ address, fromBlock, toBlock }: PastLogsOptions): Promise<Log[]> {
129+
return await this.sendRequest({
130+
method: "eth_getLogs",
131+
params: [{ fromBlock, toBlock, address }]
132+
});
133+
}
134+
135+
public async getNetworkId (): Promise<string> {
136+
return await this.sendRequest({
137+
method: "net_version",
138+
params: []
139+
});
140+
}
141+
142+
public async getBlockNumber (): Promise<number> {
143+
const result = await this.sendRequest({
144+
method: "eth_blockNumber",
145+
params: []
146+
});
147+
// return decimal
148+
return parseInt(result);
149+
}
150+
151+
public async getBalance (address: string, block: BlockSpecifier): Promise<string> {
152+
const result = await this.sendRequest({
153+
method: "eth_getBalance",
154+
params: [
155+
address,
156+
formatBlockSpecifier(block)
157+
]
158+
});
159+
// return value in decimal format
160+
return parseInt(result).toString();
161+
}
162+
163+
public async getTransactionCount (address: string, block: BlockSpecifier): Promise<string> {
164+
const result = await this.sendRequest({
165+
method: "eth_getTransactionCount",
166+
params: [
167+
address,
168+
formatBlockSpecifier(block)
169+
]
170+
});
171+
// return value in decimal format
172+
return parseInt(result).toString();
173+
}
174+
175+
public async getStorageAt (address: string, position: BN, block: BlockSpecifier): Promise<string> {
176+
return await this.sendRequest({
177+
method: "eth_getStorageAt",
178+
params: [
179+
address,
180+
position,
181+
formatBlockSpecifier(block)
182+
]
183+
});
184+
}
185+
}

packages/decoder/lib/decoders.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
} from "@truffle/codec";
2323
import * as Utils from "./utils";
2424
import type * as DecoderTypes from "./types";
25-
import Web3 from "web3";
25+
import Web3Utils from "web3-utils";
2626
import type { ContractObject as Artifact } from "@truffle/contract-schema/spec";
2727
import BN from "bn.js";
2828
import type { Provider } from "web3/providers";
@@ -33,10 +33,10 @@ import {
3333
InvalidAddressError,
3434
VariableNotFoundError,
3535
MemberNotFoundError,
36-
ArrayIndexOutOfBoundsError,
3736
NoProviderError
3837
} from "./errors";
3938
import { Shims } from "@truffle/compile-common";
39+
import { ProviderAdapter } from "./ProviderAdapter";
4040
//sorry for the untyped import, but...
4141
const SourceMapUtils = require("@truffle/source-map-utils");
4242

@@ -45,7 +45,7 @@ const SourceMapUtils = require("@truffle/source-map-utils");
4545
* @category Decoder
4646
*/
4747
export class ProjectDecoder {
48-
private web3: Web3;
48+
private providerAdapter: ProviderAdapter;
4949

5050
private compilations: Compilations.Compilation[];
5151
private contexts: Contexts.Contexts = {}; //all contexts
@@ -72,7 +72,7 @@ export class ProjectDecoder {
7272
if (!provider) {
7373
throw new NoProviderError();
7474
}
75-
this.web3 = new Web3(provider);
75+
this.providerAdapter = new ProviderAdapter(provider);
7676
this.compilations = compilations;
7777
this.ensSettings = ensSettings || {};
7878
let allocationInfo: AbiData.Allocate.ContractAllocationInfo[];
@@ -133,7 +133,7 @@ export class ProjectDecoder {
133133
): Promise<Uint8Array> {
134134
//if pending, ignore the cache
135135
if (block === "pending") {
136-
return Conversion.toBytes(await this.web3.eth.getCode(address, block));
136+
return Conversion.toBytes(await this.providerAdapter.getCode(address, block));
137137
}
138138

139139
//otherwise, start by setting up any preliminary layers as needed
@@ -145,7 +145,7 @@ export class ProjectDecoder {
145145
return this.codeCache[block][address];
146146
}
147147
//otherwise, get it, cache it, and return it
148-
let code = Conversion.toBytes(await this.web3.eth.getCode(address, block));
148+
let code = Conversion.toBytes(await this.providerAdapter.getCode(address, block));
149149
this.codeCache[block][address] = code;
150150
return code;
151151
}
@@ -163,7 +163,7 @@ export class ProjectDecoder {
163163
return "pending";
164164
}
165165

166-
return (await this.web3.eth.getBlock(block)).number;
166+
return parseInt((await this.providerAdapter.getBlockByNumber(block)).number);
167167
}
168168

169169
/**
@@ -346,7 +346,7 @@ export class ProjectDecoder {
346346
const fromBlockNumber = await this.regularizeBlock(fromBlock);
347347
const toBlockNumber = await this.regularizeBlock(toBlock);
348348

349-
const logs = await this.web3.eth.getPastLogs({
349+
const logs = await this.providerAdapter.getPastLogs({
350350
address,
351351
fromBlock: fromBlockNumber,
352352
toBlock: toBlockNumber
@@ -552,10 +552,10 @@ export class ProjectDecoder {
552552
address: string,
553553
block: DecoderTypes.BlockSpecifier = "latest"
554554
): Promise<ContractInstanceDecoder> {
555-
if (!Web3.utils.isAddress(address)) {
555+
if (!Web3Utils.isAddress(address)) {
556556
throw new InvalidAddressError(address);
557557
}
558-
address = Web3.utils.toChecksumAddress(address);
558+
address = Web3Utils.toChecksumAddress(address);
559559
const blockNumber = await this.regularizeBlock(block);
560560
const deployedBytecode = Conversion.toHexString(
561561
await this.getCode(address, blockNumber)
@@ -610,8 +610,8 @@ export class ProjectDecoder {
610610
/**
611611
* @protected
612612
*/
613-
public getWeb3(): Web3 {
614-
return this.web3;
613+
public getProviderAdapter(): ProviderAdapter {
614+
return this.providerAdapter;
615615
}
616616

617617
/**
@@ -636,7 +636,7 @@ export class ProjectDecoder {
636636
* @category Decoder
637637
*/
638638
export class ContractDecoder {
639-
private web3: Web3;
639+
private providerAdapter: ProviderAdapter;
640640

641641
private contexts: Contexts.Contexts; //note: this is deployed contexts only!
642642

@@ -669,7 +669,7 @@ export class ContractDecoder {
669669
this.contract = contract;
670670
this.compilation = compilation;
671671
this.projectDecoder = projectDecoder;
672-
this.web3 = projectDecoder.getWeb3();
672+
this.providerAdapter = projectDecoder.getProviderAdapter();
673673
this.contexts = projectDecoder.getDeployedContexts();
674674
this.userDefinedTypes = this.projectDecoder.getUserDefinedTypes();
675675

@@ -751,7 +751,7 @@ export class ContractDecoder {
751751
* @protected
752752
*/
753753
public async init(): Promise<void> {
754-
this.contractNetwork = (await this.web3.eth.net.getId()).toString();
754+
this.contractNetwork = await this.providerAdapter.getNetworkId();
755755
}
756756

757757
private get context(): Contexts.Context {
@@ -1002,7 +1002,7 @@ export class ContractDecoder {
10021002
* @category Decoder
10031003
*/
10041004
export class ContractInstanceDecoder {
1005-
private web3: Web3;
1005+
private providerAdapter: ProviderAdapter;
10061006

10071007
private compilation: Compilations.Compilation;
10081008
private contract: Compilations.Contract;
@@ -1036,12 +1036,12 @@ export class ContractInstanceDecoder {
10361036
constructor(contractDecoder: ContractDecoder, address?: string) {
10371037
this.contractDecoder = contractDecoder;
10381038
this.projectDecoder = this.contractDecoder.getProjectDecoder();
1039-
this.web3 = this.projectDecoder.getWeb3();
1039+
this.providerAdapter = this.projectDecoder.getProviderAdapter();
10401040
if (address !== undefined) {
1041-
if (!Web3.utils.isAddress(address)) {
1041+
if (!Web3Utils.isAddress(address)) {
10421042
throw new InvalidAddressError(address);
10431043
}
1044-
this.contractAddress = Web3.utils.toChecksumAddress(address);
1044+
this.contractAddress = Web3Utils.toChecksumAddress(address);
10451045
}
10461046

10471047
this.referenceDeclarations = this.projectDecoder.getReferenceDeclarations();
@@ -1076,7 +1076,7 @@ export class ContractInstanceDecoder {
10761076
this.contractCode = Conversion.toHexString(
10771077
await this.getCode(
10781078
this.contractAddress,
1079-
await this.web3.eth.getBlockNumber() //not "latest" because regularized
1079+
await this.providerAdapter.getBlockNumber() //not "latest" because regularized
10801080
)
10811081
);
10821082

@@ -1253,10 +1253,10 @@ export class ContractInstanceDecoder {
12531253
address: this.contractAddress,
12541254
code: this.contractCode,
12551255
balanceAsBN: new BN(
1256-
await this.web3.eth.getBalance(this.contractAddress, blockNumber)
1256+
await this.providerAdapter.getBalance(this.contractAddress, blockNumber)
12571257
),
12581258
nonceAsBN: new BN(
1259-
await this.web3.eth.getTransactionCount(
1259+
await this.providerAdapter.getTransactionCount(
12601260
this.contractAddress,
12611261
blockNumber
12621262
)
@@ -1383,7 +1383,7 @@ export class ContractInstanceDecoder {
13831383
//if pending, bypass the cache
13841384
if (block === "pending") {
13851385
return Conversion.toBytes(
1386-
await this.web3.eth.getStorageAt(address, slot, block),
1386+
await this.providerAdapter.getStorageAt(address, slot, block),
13871387
Codec.Evm.Utils.WORD_SIZE
13881388
);
13891389
}
@@ -1401,7 +1401,7 @@ export class ContractInstanceDecoder {
14011401
}
14021402
//otherwise, get it, cache it, and return it
14031403
let word = Conversion.toBytes(
1404-
await this.web3.eth.getStorageAt(address, slot, block),
1404+
await this.providerAdapter.getStorageAt(address, slot, block),
14051405
Codec.Evm.Utils.WORD_SIZE
14061406
);
14071407
this.storageCache[block][address][slot.toString()] = word;

packages/decoder/lib/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type BN from "bn.js";
2+
import type Web3 from "web3";
23
import type { ContractObject as Artifact } from "@truffle/contract-schema/spec";
34
import type {
45
Format,
@@ -9,7 +10,6 @@ import type {
910
ExtrasAllowed
1011
} from "@truffle/codec";
1112
import type { Provider } from "web3/providers";
12-
import type Web3 from "web3";
1313

1414
//StateVariable used to be defined here, so let's continue
1515
//to export it

0 commit comments

Comments
 (0)