Skip to content

Commit

Permalink
test: Document EOA account call behavior
Browse files Browse the repository at this point in the history
These tests assert and document the behavior of empty and eoa accounts
with value when called by external transactions with or without data.

In addition, they assert the gas usage is as expected and only depedent
on the base transaction cost plus calldata cost since no code is
executed.
  • Loading branch information
nddeluca committed Sep 6, 2024
1 parent 6e006ac commit d6f4227
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 0 deletions.
68 changes: 68 additions & 0 deletions tests/e2e-evm/test/empty_account.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import hre from "hardhat";
import { expect } from "chai";
import { Address, toHex } from "viem";
import { randomBytes } from "crypto";
import { whaleAddress } from "./addresses";

// Empty Account describes how transactions behave against an account with
// with no balance, no code, and no nonce.
//
// Accounts without code can be called with any data and value.
describe("Empty Account", function () {
const emptyAddress: Address = toHex(randomBytes(20));

// The definition of an empty account
it("has no balance, no code, and zero nonce", async function () {
const publicClient = await hre.viem.getPublicClient();

const balance = await publicClient.getBalance({ address: emptyAddress });
const code = await publicClient.getCode({ address: emptyAddress });
const nonce = await publicClient.getTransactionCount({ address: emptyAddress });

expect(balance).to.equal(0n);
expect(code).to.be.undefined;
expect(nonce).to.equal(0);
});

// An empty account can receive a 0 value transaction
it("can be called with no data or value", async function () {
const publicClient = await hre.viem.getPublicClient();
const walletClient = await hre.viem.getWalletClient(whaleAddress);

const txHash = await walletClient.sendTransaction({
to: emptyAddress,
});
const txReceipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
const tx = await publicClient.getTransaction({ hash: txHash });

expect(txReceipt.status).to.equal("success");
expect(txReceipt.gasUsed).to.equal(21000n);
expect(tx.value).to.equal(0n);
expect(tx.to).to.equal(emptyAddress);
expect(tx.input).to.equal("0x");
});

// An empty account can receive a call with any data payload
it("can be called with data", async function () {
const publicClient = await hre.viem.getPublicClient();
const walletClient = await hre.viem.getWalletClient(whaleAddress);

// 16 bytes with 1 of them zero
// 16 * 15 + 4 * 1 = 244 gas
const calldata = "0x1eb478108900a0b492ef5dd03921d02d";

const txHash = await walletClient.sendTransaction({
to: emptyAddress,
data: calldata,
});
const txReceipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
const tx = await publicClient.getTransaction({ hash: txHash });

expect(txReceipt.status).to.equal("success");
// exact gas -- no memory expansion or op code charges
expect(txReceipt.gasUsed).to.equal(21000n + 244n);
expect(tx.value).to.equal(0n);
expect(tx.to).to.equal(emptyAddress);
expect(tx.input).to.equal(calldata);
});
});
84 changes: 84 additions & 0 deletions tests/e2e-evm/test/eoa_account.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import hre from "hardhat";
import { expect } from "chai";
import { Address, toHex } from "viem";

Check failure on line 3 in tests/e2e-evm/test/eoa_account.test.ts

View workflow job for this annotation

GitHub Actions / lint / e2e-evm-lint

'Address' is defined but never used

Check failure on line 3 in tests/e2e-evm/test/eoa_account.test.ts

View workflow job for this annotation

GitHub Actions / lint / e2e-evm-lint

'toHex' is defined but never used
import { randomBytes } from "crypto";

Check failure on line 4 in tests/e2e-evm/test/eoa_account.test.ts

View workflow job for this annotation

GitHub Actions / lint / e2e-evm-lint

'randomBytes' is defined but never used
import { whaleAddress, userAddress } from "./addresses";

// EOA Account describes how transactions behave against an account with
// with no code while holding a balance.
//
// Accounts without code can be called with any data and value.
describe("EOA Account", function () {
it("has a balance and no code", async function () {
const publicClient = await hre.viem.getPublicClient();

const balance = await publicClient.getBalance({ address: userAddress });
const code = await publicClient.getCode({ address: userAddress });

expect(balance).to.not.equal(0n);
expect(code).to.be.undefined;
});

it("can be called with no data or value", async function () {
const publicClient = await hre.viem.getPublicClient();
const walletClient = await hre.viem.getWalletClient(whaleAddress);

const txHash = await walletClient.sendTransaction({
to: userAddress,
});
const txReceipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
const tx = await publicClient.getTransaction({ hash: txHash });

expect(txReceipt.status).to.equal("success");
expect(txReceipt.gasUsed).to.equal(21000n);
expect(tx.value).to.equal(0n);
expect(tx.to).to.equal(userAddress);
expect(tx.input).to.equal("0x");
});

it("can be called with data", async function () {
const publicClient = await hre.viem.getPublicClient();
const walletClient = await hre.viem.getWalletClient(whaleAddress);

// 16 bytes with 1 of them zero
// 16 * 15 + 4 * 1 = 244 gas
const calldata = "0x1eb478108900a0b492ef5dd03921d02d";

const txHash = await walletClient.sendTransaction({
to: userAddress,
data: calldata,
});
const txReceipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
const tx = await publicClient.getTransaction({ hash: txHash });

expect(txReceipt.status).to.equal("success");
// exact gas -- no memory expansion or op code charges
expect(txReceipt.gasUsed).to.equal(21000n + 244n);
expect(tx.value).to.equal(0n);
expect(tx.to).to.equal(userAddress);
expect(tx.input).to.equal(calldata);
});

it("can be called with data and value", async function () {
const publicClient = await hre.viem.getPublicClient();
const walletClient = await hre.viem.getWalletClient(whaleAddress);

// 4 non-zero bytes
// 16 * 4 = 64 gas
const calldata = "0x1eb47810";

const txHash = await walletClient.sendTransaction({
to: userAddress,
data: calldata,
});
const txReceipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
const tx = await publicClient.getTransaction({ hash: txHash });

expect(txReceipt.status).to.equal("success");
// exact gas -- no memory expansion or op code charges
expect(txReceipt.gasUsed).to.equal(21000n + 64n);
expect(tx.value).to.equal(0n);
expect(tx.to).to.equal(userAddress);
expect(tx.input).to.equal(calldata);
});
});

0 comments on commit d6f4227

Please sign in to comment.