From a4deb4fd1b28db219a9a89f2fce2e4842434fac9 Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Wed, 5 Jun 2024 21:25:48 +0530 Subject: [PATCH] StorageReadable unit tests (#154) Foundry unit tests for StorageReadable contract. Closes #146. --- test/reader/StorageReadable.spec.ts | 84 ----------------------------- test/reader/StorageReadable.t.sol | 59 ++++++++++++++++++++ 2 files changed, 59 insertions(+), 84 deletions(-) delete mode 100644 test/reader/StorageReadable.spec.ts create mode 100644 test/reader/StorageReadable.t.sol diff --git a/test/reader/StorageReadable.spec.ts b/test/reader/StorageReadable.spec.ts deleted file mode 100644 index 21b7b852..00000000 --- a/test/reader/StorageReadable.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { expect } from "chai"; -import { BigNumber, BigNumberish, ContractFactory } from "ethers"; -import { ethers } from "hardhat"; - -describe("StorageReadable", () => { - const fromHex = (data: string, start?: number, end?: number) => - BigNumber.from(ethers.utils.hexDataSlice(data, start || 0, end)); - const keccak = (numbers: BigNumberish[]) => - ethers.utils.solidityKeccak256( - numbers.map(() => "uint256"), - numbers, - ); - - let StorageAccessibleWrapper: ContractFactory; - - before(async () => { - StorageAccessibleWrapper = await ethers.getContractFactory( - "StorageAccessibleWrapper", - ); - }); - - describe("getStorageAt", async () => { - it("can read statically sized words", async () => { - const instance = await StorageAccessibleWrapper.deploy(); - await instance.setFoo(42); - - expect( - await instance.getStorageAt(await instance.SLOT_FOO(), 1), - ).to.equal(ethers.utils.solidityPack(["uint256"], [42])); - }); - - it("can read fields that are packed into single storage slot", async () => { - const instance = await StorageAccessibleWrapper.deploy(); - await instance.setBar(7); - await instance.setBam(13); - - const data = await instance.getStorageAt(await instance.SLOT_BAR(), 1); - expect(data).to.equal( - ethers.utils.hexZeroPad( - ethers.utils.solidityPack(["uint64", "uint128"], [13, 7]), - 32, - ), - ); - }); - - it("can read arrays in one go", async () => { - const instance = await StorageAccessibleWrapper.deploy(); - const slot = await instance.SLOT_BAZ(); - await instance.setBaz([42, 1337]); - - const length = await instance.getStorageAt(slot, 1); - expect(BigNumber.from(length)).to.equal(2); - - const data = await instance.getStorageAt(keccak([slot]), length); - expect(fromHex(data, 0, 32)).to.equal(42); - expect(fromHex(data, 32, 64)).to.equal(1337); - }); - - it("can read mappings", async () => { - const instance = await StorageAccessibleWrapper.deploy(); - await instance.setQuxKeyValue(42, 69); - expect( - fromHex( - await instance.getStorageAt( - keccak([42, await instance.SLOT_QUX()]), - 1, - ), - ), - ).to.equal(69); - }); - - it("can read structs", async () => { - const instance = await StorageAccessibleWrapper.deploy(); - await instance.setFoobar(19, 21); - - const packed = await instance.getStorageAt( - await instance.SLOT_FOOBAR(), - 10, - ); - expect(fromHex(packed, 0, 32)).to.equal(19); - expect(fromHex(packed, 32, 64)).to.equal(21); - }); - }); -}); diff --git a/test/reader/StorageReadable.t.sol b/test/reader/StorageReadable.t.sol new file mode 100644 index 00000000..842193b4 --- /dev/null +++ b/test/reader/StorageReadable.t.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity >=0.7.6 <0.9.0; +pragma abicoder v2; + +import {Test} from "forge-std/Test.sol"; +import {StorageAccessibleWrapper} from "src/contracts/test/vendor/StorageAccessibleWrapper.sol"; + +contract StorageReadableTest is Test { + StorageAccessibleWrapper instance; + + function setUp() public { + instance = new StorageAccessibleWrapper(); + } + + function test_can_read_statically_sized_words() public { + instance.setFoo(42); + bytes memory actualBytes = instance.getStorageAt(instance.SLOT_FOO(), 1); + bytes memory expectedBytes = abi.encode(42); + assertEq(actualBytes, expectedBytes); + } + + function test_can_read_fields_that_are_packed_into_single_storage_slot() public { + instance.setBar(7); + instance.setBam(13); + bytes memory actualBytes = instance.getStorageAt(instance.SLOT_BAR(), 1); + bytes memory expectedBytes = abi.encodePacked(new bytes(8), uint64(13), uint128(7)); + assertEq(actualBytes, expectedBytes); + } + + function test_can_read_arrays_in_one_go() public { + uint8 slot = instance.SLOT_BAZ(); + uint256[] memory arr = new uint256[](2); + arr[0] = 42; + arr[1] = 1337; + instance.setBaz(arr); + bytes memory data = instance.getStorageAt(slot, 1); + uint256 length = abi.decode(data, (uint256)); + assertEq(length, 2); + bytes memory packed = instance.getStorageAt(uint256(keccak256(abi.encode(slot))), length); + (uint256 firstValue, uint256 secondValue) = abi.decode(packed, (uint256, uint256)); + assertEq(firstValue, 42); + assertEq(secondValue, 1337); + } + + function test_can_read_mappings() public { + instance.setQuxKeyValue(42, 69); + bytes memory data = instance.getStorageAt(uint256(keccak256(abi.encode([42, instance.SLOT_QUX()]))), 1); + uint256 value = abi.decode(data, (uint256)); + assertEq(value, 69); + } + + function test_can_read_structs() public { + instance.setFoobar(19, 21); + bytes memory packed = instance.getStorageAt(instance.SLOT_FOOBAR(), 10); + (uint256 firstValue, uint256 secondValue) = abi.decode(packed, (uint256, uint256)); + assertEq(firstValue, 19); + assertEq(secondValue, 21); + } +}