Skip to content

Commit

Permalink
Add support for EIP-2930 txs to LocalAccountsProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
fvictorio committed Apr 8, 2021
1 parent 38a576e commit 9194756
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 8 deletions.
33 changes: 26 additions & 7 deletions packages/hardhat-core/src/internal/core/providers/accounts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Transaction as TransactionT } from "@ethereumjs/tx";
import { BN } from "ethereumjs-util";
import * as t from "io-ts";

Expand Down Expand Up @@ -188,9 +187,11 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
chainId: number,
privateKey: Buffer
): Promise<Buffer> {
const chains = await import("@ethereumjs/common/dist/chains");
const { chains } = await import("@ethereumjs/common/dist/chains");

const { Transaction } = await import("@ethereumjs/tx");
const { AccessListEIP2930Transaction, Transaction } = await import(
"@ethereumjs/tx"
);

const { default: Common } = await import("@ethereumjs/common");

Expand All @@ -200,18 +201,36 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
};

const common =
chains.chains.names[chainId] !== undefined
? new Common({ chain: chainId })
chains.names[chainId] !== undefined
? new Common({ chain: chainId, hardfork: "berlin" })
: Common.forCustomChain(
"mainnet",
{
chainId,
networkId: chainId,
},
"istanbul"
"berlin"
);

const transaction = Transaction.fromTxData(txData, { common });
let transaction;
if (txData.accessList !== undefined) {
// we convert the access list to the type
// that AccessListEIP2930Transaction expects
const accessList = txData.accessList.map(
({ address, storageKeys }) =>
[address, storageKeys] as [Buffer, Buffer[]]
);

transaction = AccessListEIP2930Transaction.fromTxData(
{
...txData,
accessList,
},
{ common }
);
} else {
transaction = Transaction.fromTxData(txData, { common });
}

const signedTransaction = transaction.sign(privateKey);

Expand Down
84 changes: 83 additions & 1 deletion packages/hardhat-core/test/internal/core/providers/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ function privateKeyToAddress(privateKey: string): string {
return bufferToHex(privateToAddress(toBuffer(privateKey))).toLowerCase();
}

const MOCK_PROVIDER_CHAIN_ID = 123;

describe("Local accounts provider", () => {
let mock: MockedProvider;
let wrapper: EIP1193Provider;
Expand All @@ -35,7 +37,10 @@ describe("Local accounts provider", () => {

beforeEach(() => {
mock = new MockedProvider();
mock.setReturnValue("net_version", numberToRpcQuantity(123));
mock.setReturnValue(
"net_version",
numberToRpcQuantity(MOCK_PROVIDER_CHAIN_ID)
);
mock.setReturnValue("eth_getTransactionCount", numberToRpcQuantity(0x8));
mock.setReturnValue("eth_accounts", []);

Expand Down Expand Up @@ -166,6 +171,83 @@ describe("Local accounts provider", () => {
assert.equal(mock.getNumberOfCalls("eth_getTransactionCount"), 1);
});

it("should send access list transactions", async () => {
await wrapper.request({
method: "eth_sendTransaction",
params: [
{
from: "0xb5bc06d4548a3ac17d72b372ae1e416bf65b8ead",
to: "0xb5bc06d4548a3ac17d72b372ae1e416bf65b8ead",
gas: numberToRpcQuantity(30000),
gasPrice: numberToRpcQuantity(1),
nonce: numberToRpcQuantity(0),
value: numberToRpcQuantity(1),
chainId: numberToRpcQuantity(MOCK_PROVIDER_CHAIN_ID),
accessList: [
{
address: "0x57d7aD4D3F0C74e3766874CF06fA1DC23C21f7E8",
storageKeys: [
"0xa50e92910457911e0e22d6dd1672f440a37b590b231d8309101255290f5394ec",
],
},
],
},
],
});

const rawTransaction = mock.getLatestParams("eth_sendRawTransaction")[0];

// this is a valid raw EIP_2930 tx
// checked in a local hardhat node, where the sender account
// had funds and the chain id was 123
const expectedRaw =
"0x01f89a7b800182753094b5bc06d4548a3ac17d72b372ae1e416bf65b8e" +
"ad0180f838f79457d7ad4d3f0c74e3766874cf06fa1dc23c21f7e8e1a0a5" +
"0e92910457911e0e22d6dd1672f440a37b590b231d8309101255290f5394" +
"ec80a02b2fca5e2cf3569d29693e965f045529efa6a54bf0ab11104dd4ea" +
"8b2ca3daf7a06025c30f36a179a09b9952e025632a65f220ec385eccd23a" +
"1fb952976eace481";

assert.equal(rawTransaction, expectedRaw);
});

it("should add the chainId value if it's missing", async () => {
await wrapper.request({
method: "eth_sendTransaction",
params: [
{
from: "0xb5bc06d4548a3ac17d72b372ae1e416bf65b8ead",
to: "0xb5bc06d4548a3ac17d72b372ae1e416bf65b8ead",
gas: numberToRpcQuantity(30000),
gasPrice: numberToRpcQuantity(1),
nonce: numberToRpcQuantity(0),
value: numberToRpcQuantity(1),
accessList: [
{
address: "0x57d7aD4D3F0C74e3766874CF06fA1DC23C21f7E8",
storageKeys: [
"0xa50e92910457911e0e22d6dd1672f440a37b590b231d8309101255290f5394ec",
],
},
],
},
],
});

const rawTransaction = mock.getLatestParams("eth_sendRawTransaction")[0];

// see previous test
const expectedRaw =
"0x01f89a7b800182753094b5bc06d4548a3ac17d72b372ae1e416bf65b8e" +
"ad0180f838f79457d7ad4d3f0c74e3766874cf06fa1dc23c21f7e8e1a0a5" +
"0e92910457911e0e22d6dd1672f440a37b590b231d8309101255290f5394" +
"ec80a02b2fca5e2cf3569d29693e965f045529efa6a54bf0ab11104dd4ea" +
"8b2ca3daf7a06025c30f36a179a09b9952e025632a65f220ec385eccd23a" +
"1fb952976eace481";

assert.equal(rawTransaction, expectedRaw);
});

describe("eth_sign", () => {
it("Should be compatible with parity's implementation", async () => {
// This test was created by using Parity Ethereum
Expand Down

0 comments on commit 9194756

Please sign in to comment.