Skip to content

Commit c47d2eb

Browse files
committed
Added EIP-2930 support (ethers-io#1364).
1 parent 1db4ce1 commit c47d2eb

File tree

13 files changed

+487
-40
lines changed

13 files changed

+487
-40
lines changed

packages/abstract-provider/src.ts/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
44
import { BytesLike, isHexString } from "@ethersproject/bytes";
55
import { Network } from "@ethersproject/networks";
66
import { Deferrable, Description, defineReadOnly } from "@ethersproject/properties";
7-
import { Transaction } from "@ethersproject/transactions";
7+
import { AccessListish, Transaction } from "@ethersproject/transactions";
88
import { OnceBlockable } from "@ethersproject/web";
99

1010
import { Logger } from "@ethersproject/logger";
@@ -26,6 +26,9 @@ export type TransactionRequest = {
2626
data?: BytesLike,
2727
value?: BigNumberish,
2828
chainId?: number
29+
30+
type?: number;
31+
accessList?: AccessListish;
2932
}
3033

3134
export interface TransactionResponse extends Transaction {

packages/abstract-signer/src.ts/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { version } from "./_version";
1010
const logger = new Logger(version);
1111

1212
const allowedTransactionKeys: Array<string> = [
13-
"chainId", "data", "from", "gasLimit", "gasPrice", "nonce", "to", "value"
13+
"accessList", "chainId", "data", "from", "gasLimit", "gasPrice", "nonce", "to", "type", "value"
1414
];
1515

1616
const forwardErrors = [

packages/bytes/src.ts/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,8 @@ export function splitSignature(signature: SignatureLike): Signature {
390390
if (result.recoveryParam == null) {
391391
if (result.v == null) {
392392
logger.throwArgumentError("signature missing v and recoveryParam", "signature", signature);
393+
} else if (result.v === 0 || result.v === 1) {
394+
result.recoveryParam = result.v;
393395
} else {
394396
result.recoveryParam = 1 - (result.v % 2);
395397
}

packages/contracts/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"@ethersproject/bytes": "^5.0.9",
1010
"@ethersproject/constants": "^5.0.8",
1111
"@ethersproject/logger": "^5.0.8",
12-
"@ethersproject/properties": "^5.0.7"
12+
"@ethersproject/properties": "^5.0.7",
13+
"@ethersproject/transactions": "^5.0.11"
1314
},
1415
"description": "Contract abstraction meta-class for ethers.",
1516
"ethereum": "donations.ethers.eth",

packages/contracts/src.ts/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getAddress, getContractAddress } from "@ethersproject/address";
77
import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
88
import { arrayify, BytesLike, concat, hexlify, isBytes, isHexString } from "@ethersproject/bytes";
99
import { Deferrable, defineReadOnly, deepCopy, getStatic, resolveProperties, shallowCopy } from "@ethersproject/properties";
10-
// @TOOD remove dependences transactions
10+
import { AccessList, accessListify, AccessListish } from "@ethersproject/transactions";
1111

1212
import { Logger } from "@ethersproject/logger";
1313
import { version } from "./_version";
@@ -18,6 +18,8 @@ export interface Overrides {
1818
gasLimit?: BigNumberish | Promise<BigNumberish>;
1919
gasPrice?: BigNumberish | Promise<BigNumberish>;
2020
nonce?: BigNumberish | Promise<BigNumberish>;
21+
type?: number;
22+
accessList?: AccessListish;
2123
};
2224

2325
export interface PayableOverrides extends Overrides {
@@ -45,6 +47,9 @@ export interface PopulatedTransaction {
4547
data?: string;
4648
value?: BigNumber;
4749
chainId?: number;
50+
51+
type?: number;
52+
accessList?: AccessList;
4853
};
4954

5055
export type EventFilter = {
@@ -94,7 +99,8 @@ export interface ContractTransaction extends TransactionResponse {
9499
///////////////////////////////
95100

96101
const allowedTransactionKeys: { [ key: string ]: boolean } = {
97-
chainId: true, data: true, from: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
102+
chainId: true, data: true, from: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true,
103+
type: true, accessList: true,
98104
}
99105

100106
async function resolveName(resolver: Signer | Provider, nameOrPromise: string | Promise<string>): Promise<string> {
@@ -212,6 +218,9 @@ async function populateTransaction(contract: Contract, fragment: FunctionFragmen
212218
if (ro.gasPrice != null) { tx.gasPrice = BigNumber.from(ro.gasPrice); }
213219
if (ro.from != null) { tx.from = ro.from; }
214220

221+
if (ro.type != null) { tx.type = ro.type; }
222+
if (ro.accessList != null) { tx.accessList = accessListify(ro.accessList); }
223+
215224
// If there was no "gasLimit" override, but the ABI specifies a default, use it
216225
if (tx.gasLimit == null && fragment.gas != null) {
217226
// Conmpute the intrinisic gas cost for this transaction
@@ -247,6 +256,9 @@ async function populateTransaction(contract: Contract, fragment: FunctionFragmen
247256
delete overrides.from;
248257
delete overrides.value;
249258

259+
delete overrides.type;
260+
delete overrides.accessList;
261+
250262
// Make sure there are no stray overrides, which may indicate a
251263
// typo or using an unsupported key.
252264
const leftovers = Object.keys(overrides).filter((key) => ((<any>overrides)[key] != null));

packages/networks/src.ts/index.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,30 @@ function ethDefaultProvider(network: string | Network): Renetworkable {
3939
}
4040

4141
if (providers.AlchemyProvider) {
42+
// These networks are currently faulty on Alchemy as their
43+
// network does not handle the Berlin hardfork, which is
44+
// live on these ones.
45+
// @TODO: This goes away once AlchemyAPI has upgraded their nodes
46+
const skip = [ "goerli", "ropsten", "rinkeby" ];
4247
try {
43-
providerList.push(new providers.AlchemyProvider(network, options.alchemy));
48+
const provider = new providers.AlchemyProvider(network, options.alchemy);
49+
if (provider.network && skip.indexOf(provider.network.name) === -1) {
50+
providerList.push(provider);
51+
}
4452
} catch(error) { }
4553
}
4654

4755
if (providers.PocketProvider) {
56+
// These networks are currently faulty on Alchemy as their
57+
// network does not handle the Berlin hardfork, which is
58+
// live on these ones.
59+
// @TODO: This goes away once Pocket has upgraded their nodes
60+
const skip = [ "goerli", "ropsten", "rinkeby" ];
4861
try {
49-
providerList.push(new providers.PocketProvider(network));
62+
const provider = new providers.PocketProvider(network);
63+
if (provider.network && skip.indexOf(provider.network.name) === -1) {
64+
providerList.push(provider);
65+
}
5066
} catch(error) { }
5167
}
5268

packages/providers/src.ts/base-provider.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,15 @@ export class BaseProvider extends Provider implements EnsProvider {
11221122
tx[key] = Promise.resolve(values[key]).then((v) => (v ? BigNumber.from(v): null));
11231123
});
11241124

1125+
["type"].forEach((key) => {
1126+
if (values[key] == null) { return; }
1127+
tx[key] = Promise.resolve(values[key]).then((v) => ((v != null) ? v: null));
1128+
});
1129+
1130+
if (values.accessList) {
1131+
tx.accessList = this.formatter.accessList(values.accessList);
1132+
}
1133+
11251134
["data"].forEach((key) => {
11261135
if (values[key] == null) { return; }
11271136
tx[key] = Promise.resolve(values[key]).then((v) => (v ? hexlify(v): null));
@@ -1175,6 +1184,7 @@ export class BaseProvider extends Provider implements EnsProvider {
11751184
const params = await resolveProperties({
11761185
transaction: this._getTransactionRequest(transaction)
11771186
});
1187+
11781188
const result = await this.perform("estimateGas", params);
11791189
try {
11801190
return BigNumber.from(result);

packages/providers/src.ts/etherscan-provider.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ function getTransactionPostData(transaction: TransactionRequest): Record<string,
2020
const result: Record<string, string> = { };
2121
for (let key in transaction) {
2222
if ((<any>transaction)[key] == null) { continue; }
23-
let value = hexlify((<any>transaction)[key]);
23+
let value = (<any>transaction)[key];
2424
// Quantity-types require no leading zero, unless 0
25-
if ((<any>{ gasLimit: true, gasPrice: true, nonce: true, value: true })[key]) {
26-
value = hexValue(value);
25+
if ((<any>{ type: true, gasLimit: true, gasPrice: true, nonce: true, value: true })[key]) {
26+
value = hexValue(hexlify(value));
27+
} else if (key === "accessList") {
28+
value = value;
29+
} else {
30+
value = hexlify(value);
2731
}
2832
result[key] = value;
2933
}
@@ -293,6 +297,13 @@ export class EtherscanProvider extends BaseProvider{
293297

294298

295299
case "call": {
300+
if (params.transaction.type != null) {
301+
logger.throwError("Etherscan does not currently support Berlin", Logger.errors.UNSUPPORTED_OPERATION, {
302+
operation: "call",
303+
transaction: params.transaction
304+
});
305+
}
306+
296307
if (params.blockTag !== "latest") {
297308
throw new Error("EtherscanProvider does not support blockTag for call");
298309
}
@@ -310,6 +321,13 @@ export class EtherscanProvider extends BaseProvider{
310321
}
311322

312323
case "estimateGas": {
324+
if (params.transaction.type != null) {
325+
logger.throwError("Etherscan does not currently support Berlin", Logger.errors.UNSUPPORTED_OPERATION, {
326+
operation: "estimateGas",
327+
transaction: params.transaction
328+
});
329+
}
330+
313331
const postData = getTransactionPostData(params.transaction);
314332
postData.module = "proxy";
315333
postData.action = "eth_estimateGas";

packages/providers/src.ts/formatter.ts

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { BigNumber } from "@ethersproject/bignumber";
66
import { hexDataLength, hexDataSlice, hexValue, hexZeroPad, isHexString } from "@ethersproject/bytes";
77
import { AddressZero } from "@ethersproject/constants";
88
import { shallowCopy } from "@ethersproject/properties";
9-
import { parse as parseTransaction } from "@ethersproject/transactions";
9+
import { AccessList, accessListify, parse as parseTransaction } from "@ethersproject/transactions";
1010

1111
import { Logger } from "@ethersproject/logger";
1212
import { version } from "./_version";
@@ -51,6 +51,9 @@ export class Formatter {
5151
formats.transaction = {
5252
hash: hash,
5353

54+
type: Formatter.allowNull(number, null),
55+
accessList: Formatter.allowNull(this.accessList.bind(this), null),
56+
5457
blockHash: Formatter.allowNull(hash, null),
5558
blockNumber: Formatter.allowNull(number, null),
5659
transactionIndex: Formatter.allowNull(number, null),
@@ -83,6 +86,8 @@ export class Formatter {
8386
to: Formatter.allowNull(address),
8487
value: Formatter.allowNull(bigNumber),
8588
data: Formatter.allowNull(strictData),
89+
type: Formatter.allowNull(number),
90+
accessList: Formatter.allowNull(this.accessList.bind(this), null),
8691
};
8792

8893
formats.receiptLog = {
@@ -162,6 +167,10 @@ export class Formatter {
162167
return formats;
163168
}
164169

170+
accessList(accessList: Array<any>): AccessList {
171+
return accessListify(accessList || []);
172+
}
173+
165174
// Requires a BigNumberish that is within the IEEE754 safe integer range; returns a number
166175
// Strict! Used on input.
167176
number(number: any): number {
@@ -308,28 +317,9 @@ export class Formatter {
308317
transaction.creates = this.contractAddress(transaction);
309318
}
310319

311-
// @TODO: use transaction.serialize? Have to add support for including v, r, and s...
312-
/*
313-
if (!transaction.raw) {
314-
315-
// Very loose providers (e.g. TestRPC) do not provide a signature or raw
316-
if (transaction.v && transaction.r && transaction.s) {
317-
let raw = [
318-
stripZeros(hexlify(transaction.nonce)),
319-
stripZeros(hexlify(transaction.gasPrice)),
320-
stripZeros(hexlify(transaction.gasLimit)),
321-
(transaction.to || "0x"),
322-
stripZeros(hexlify(transaction.value || "0x")),
323-
hexlify(transaction.data || "0x"),
324-
stripZeros(hexlify(transaction.v || "0x")),
325-
stripZeros(hexlify(transaction.r)),
326-
stripZeros(hexlify(transaction.s)),
327-
];
328-
329-
transaction.raw = rlpEncode(raw);
330-
}
320+
if (transaction.type === 1 && transaction.accessList == null) {
321+
transaction.accessList = [ ];
331322
}
332-
*/
333323

334324
const result: TransactionResponse = Formatter.check(this.formats.transaction, transaction);
335325

packages/providers/src.ts/json-rpc-provider.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { _TypedDataEncoder } from "@ethersproject/hash";
1010
import { Network, Networkish } from "@ethersproject/networks";
1111
import { checkProperties, deepCopy, Deferrable, defineReadOnly, getStatic, resolveProperties, shallowCopy } from "@ethersproject/properties";
1212
import { toUtf8Bytes } from "@ethersproject/strings";
13+
import { AccessList, accessListify } from "@ethersproject/transactions";
1314
import { ConnectionInfo, fetchJson, poll } from "@ethersproject/web";
1415

1516
import { Logger } from "@ethersproject/logger";
@@ -264,7 +265,8 @@ class UncheckedJsonRpcSigner extends JsonRpcSigner {
264265
}
265266

266267
const allowedTransactionKeys: { [ key: string ]: boolean } = {
267-
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
268+
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true,
269+
type: true, accessList: true
268270
}
269271

270272
export class JsonRpcProvider extends BaseProvider {
@@ -529,20 +531,21 @@ export class JsonRpcProvider extends BaseProvider {
529531
// before this is called
530532
// @TODO: This will likely be removed in future versions and prepareRequest
531533
// will be the preferred method for this.
532-
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: { [key: string]: boolean }): { [key: string]: string } {
534+
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: { [key: string]: boolean }): { [key: string]: string | AccessList } {
533535
// Check only allowed properties are given
534536
const allowed = shallowCopy(allowedTransactionKeys);
535537
if (allowExtra) {
536538
for (const key in allowExtra) {
537539
if (allowExtra[key]) { allowed[key] = true; }
538540
}
539541
}
542+
540543
checkProperties(transaction, allowed);
541544

542-
const result: { [key: string]: string } = {};
545+
const result: { [key: string]: string | AccessList } = {};
543546

544547
// Some nodes (INFURA ropsten; INFURA mainnet is fine) do not like leading zeros.
545-
["gasLimit", "gasPrice", "nonce", "value"].forEach(function(key) {
548+
["gasLimit", "gasPrice", "type", "nonce", "value"].forEach(function(key) {
546549
if ((<any>transaction)[key] == null) { return; }
547550
const value = hexValue((<any>transaction)[key]);
548551
if (key === "gasLimit") { key = "gas"; }
@@ -554,6 +557,10 @@ export class JsonRpcProvider extends BaseProvider {
554557
result[key] = hexlify((<any>transaction)[key]);
555558
});
556559

560+
if ((<any>transaction).accessList) {
561+
result["accessList"] = accessListify((<any>transaction).accessList);
562+
}
563+
557564
return result;
558565
}
559566
}

0 commit comments

Comments
 (0)