diff --git a/.github/workflows/foundry-storage-check.yml b/.github/workflows/foundry-storage-check.yml index fd43b3b..38e4ec3 100644 --- a/.github/workflows/foundry-storage-check.yml +++ b/.github/workflows/foundry-storage-check.yml @@ -28,6 +28,6 @@ jobs: version: nightly - name: Check storage layout - uses: Rubilmax/foundry-storage-check@v2.1 + uses: Rubilmax/foundry-storage-check@v3 with: contract: contracts/Example.sol:Example diff --git a/README.md b/README.md index e2cfd69..402175b 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,11 @@ ## Live Example -Checkout [PR #21](/pulls/21) for a live example: +Check out [PR #21](/pulls/21) for a live example: - Action is ran on [contracts/Example.sol:Example](./contracts/Example.sol) - Warnings & errors appear on the [Pull Request changes](https://github.com/Rubilmax/foundry-storage-check/pull/21/files) -## How it works - -Everytime somebody opens a Pull Request, the action runs [Foundry](https://github.com/foundry-rs/foundry) `forge` to generate the storage layout of the Smart Contract you want to check. - -Once generated, the action will fetch the comparative storage layout stored as an artifact from previous runs; parse & compare them, pinning warnings and errors on the Pull Request. - ## Getting started ### Automatically generate & compare to the previous storage layout on every PR @@ -52,15 +46,35 @@ jobs: version: nightly - name: Check storage layout - uses: Rubilmax/foundry-storage-check@v2.1 + uses: Rubilmax/foundry-storage-check@v3 with: contract: src/Contract.sol:Contract + # settings below are optional, but allows to check whether the added storage slots are empty on the deployed contract + rpcUrl: wss://eth-mainnet.g.alchemy.com/v2/ # the RPC url to use to query the deployed contract's storage slots + address: 0x0000000000000000000000000000000000000000 # the address at which the contract check is deployed + failOnRemoval: true # fail the CI when removing storage slots (default: false) ``` > :information_source: **An error will appear at first run!**
> 🔴 **Error:** No workflow run found with an artifact named "..."
> As the action is expecting a comparative file stored on the base branch and cannot find it (because the action never ran on the target branch and thus has never uploaded any storage layout) +--- + +## How it works + +Everytime somebody opens a Pull Request, the action runs [Foundry](https://github.com/foundry-rs/foundry) `forge` to generate the storage layout of the Smart Contract you want to check. + +Once generated, the action will fetch the comparative storage layout stored as an artifact from previous runs and compare them, to perform a series of checks at each storage byte, and raise a notice accordingly: + +- Variable changed: `error` +- Type definition changed: `error` +- Type definition removed: `warning` +- Different variable naming: `warning` +- Variable removed (optional): `error` + +--- + ## Options ### `contract` _{string}_ @@ -69,6 +83,20 @@ The path and name of the contract of which to inspect storage layout (e.g. src/C _Required_ +### `address` _{string}_ + +The address at which the contract is deployed on the EVM-compatible chain queried via `rpcUrl`. + +### `rpcUrl` _{string}_ + +The HTTP/WS url used to query the EVM-compatible chain for storage slots to check for clashing. + +### `failOnRemoval` _{string}_ + +Whether to fail the CI when removing a storage slot (to only allow added or renamed storage slots). + +_Defaults to: `false`_ + ### `base` _{string}_ The gas diff reference branch name, used to fetch the previous gas report to compare the freshly generated gas report to. diff --git a/action.yml b/action.yml index 7484ad8..464c038 100644 --- a/action.yml +++ b/action.yml @@ -9,6 +9,16 @@ inputs: contract: description: The path and name of the contract of which to inspect storage layout (e.g. src/Contract.sol:Contract). required: true + address: + description: The address at which the contract is deployed on the EVM-compatible chain queried via rpcUrl. + required: false + rpcUrl: + description: The HTTP/WS url used to query the EVM-compatible chain for storage slots to check for clashing. + required: false + failOnRemoval: + description: Whether to fail the CI when removing a storage slot (to only allow added or renamed storage slots). + required: false + default: false token: description: The repository's github token. default: ${{ github.token }} diff --git a/package.json b/package.json index 5f85aee..c242288 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "foundry-storage-check", - "version": "2.1.0", + "version": "3.0.0", "description": "Github Action checking the storage layout diff from Foundry storage layout reports", "author": { "name": "Romain (Rubilmax) Milon", @@ -43,9 +43,11 @@ "@actions/artifact": "^1.1.0", "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", + "@ethersproject/providers": "^5.7.2", + "@ethersproject/solidity": "^5.7.0", "@octokit/core": "^4.1.0", "@solidity-parser/parser": "^0.14.5", - "keccak256": "^1.0.6", + "js-sha3": "^0.8.0", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/src/check.ts b/src/check.ts index b25832f..0f601dc 100644 --- a/src/check.ts +++ b/src/check.ts @@ -1,18 +1,22 @@ -import keccak256 from "keccak256"; import _isEqual from "lodash/isEqual"; import _range from "lodash/range"; +import _sortBy from "lodash/sortBy"; import _uniqWith from "lodash/uniqWith"; +import { Provider } from "@ethersproject/providers"; +import { keccak256 } from "@ethersproject/solidity"; + import { + StorageLayoutDiffAdded, StorageLayoutDiff, StorageLayoutDiffType, StorageLayoutReportExact, StorageVariableDetails, StorageVariableExact, + StorageLayoutDiffAddedNonZeroSlot, } from "./types"; export const STORAGE_WORD_SIZE = 32n; -export const ZERO_BYTE = "".padStart(64, "0"); interface StorageBytesMapping { [byte: string]: StorageVariableDetails; @@ -29,7 +33,7 @@ const getStorageVariableBytesMapping = ( let example = {}; switch (varType.encoding) { case "dynamic_array": - slot = BigInt("0x" + keccak256("0x" + variable.slot.toString(16)).toString("hex")); // slot of the element at index 0 + slot = BigInt(keccak256(["uint256"], [variable.slot])); // slot of the element at index 0 example = getStorageVariableBytesMapping( layout, { @@ -39,13 +43,11 @@ const getStorageVariableBytesMapping = ( type: varType.base!, label: variable.label.replace("[]", "[0]"), }, - startByte + slot * STORAGE_WORD_SIZE + slot * STORAGE_WORD_SIZE ); break; case "mapping": - slot = BigInt( - "0x" + keccak256("0x" + ZERO_BYTE + variable.slot.toString(16)).toString("hex") - ); // slot of the element at key 0 + slot = BigInt(keccak256(["uint256", "uint256"], [0, variable.slot])); // slot of the element at key 0 example = getStorageVariableBytesMapping( layout, { @@ -55,7 +57,7 @@ const getStorageVariableBytesMapping = ( type: varType.value!, label: `${variable.label}[0]`, }, - startByte + slot * STORAGE_WORD_SIZE + slot * STORAGE_WORD_SIZE ); break; default: @@ -65,7 +67,7 @@ const getStorageVariableBytesMapping = ( const details: StorageVariableDetails = { ...variable, fullLabel: variable.parent - ? `(${variable.parent.typeLabel})${variable.parent.label}.${variable.label}` + ? `(${variable.parent.typeLabel} ${variable.parent.label}).${variable.label}` : variable.label, typeLabel: varType.label.replace(/struct /, ""), startByte, @@ -112,11 +114,17 @@ const getStorageBytesMapping = (layout: StorageLayoutReportExact): StorageBytesM {} ); -export const checkLayouts = ( +export const checkLayouts = async ( srcLayout: StorageLayoutReportExact, - cmpLayout: StorageLayoutReportExact -): StorageLayoutDiff[] => { + cmpLayout: StorageLayoutReportExact, + { + address, + provider, + checkRemovals, + }: { address?: string; provider?: Provider; checkRemovals?: boolean } = {} +): Promise => { const diffs: StorageLayoutDiff[] = []; + const added: StorageLayoutDiffAdded[] = []; const srcMapping = getStorageBytesMapping(srcLayout); const cmpMapping = getStorageBytesMapping(cmpLayout); @@ -125,7 +133,21 @@ export const checkLayouts = ( const srcSlotVar = srcMapping[byte]; const cmpSlotVar = cmpMapping[byte]; - if (!srcSlotVar) continue; // source byte was unused + const byteIndex = BigInt(byte); + const location = { + slot: byteIndex / STORAGE_WORD_SIZE, + offset: byteIndex % STORAGE_WORD_SIZE, + }; + + if (!srcSlotVar) { + added.push({ + location, + cmp: cmpSlotVar, + }); + + continue; // source byte was unused + } + if ( cmpSlotVar.type === srcSlotVar.type && cmpSlotVar.fullLabel === srcSlotVar.fullLabel && @@ -134,18 +156,30 @@ export const checkLayouts = ( cmpSlotVar.startByte === srcSlotVar.startByte ) continue; // variable did not change - if (srcSlotVar.label === "__gap" || cmpSlotVar.label === "__gap") continue; // source byte was part of a gap slot or is replaced with a gap slot + + if (srcSlotVar.label === "__gap" || cmpSlotVar.label === "__gap") { + added.push({ + location, + cmp: cmpSlotVar, + }); + + continue; // source byte was part of a gap slot or is replaced with a gap slot + } if (cmpSlotVar.fullLabel !== srcSlotVar.fullLabel) { - if (cmpSlotVar.fullLabel.startsWith(`(${srcSlotVar.typeLabel})${srcSlotVar.label}`)) continue; // variable is a member of source struct, in empty bytes + if (cmpSlotVar.fullLabel.startsWith(`(${srcSlotVar.typeLabel} ${srcSlotVar.label})`)) { + added.push({ + location, + cmp: cmpSlotVar, + }); + + continue; // variable is a member of source struct, in empty bytes + } if (cmpSlotVar.type === srcSlotVar.type) { if (cmpSlotVar.label !== srcSlotVar.label) diffs.push({ - location: { - slot: srcSlotVar.slot, - offset: srcSlotVar.offset, - }, + location, type: StorageLayoutDiffType.LABEL, src: srcSlotVar, cmp: cmpSlotVar, @@ -155,10 +189,7 @@ export const checkLayouts = ( } diffs.push({ - location: { - slot: srcSlotVar.slot, - offset: srcSlotVar.offset, - }, + location, type: StorageLayoutDiffType.VARIABLE, src: srcSlotVar, cmp: cmpSlotVar, @@ -202,10 +233,7 @@ export const checkLayouts = ( } diffs.push({ - location: { - slot: srcSlotVar.slot, - offset: srcSlotVar.offset, - }, + location, type: StorageLayoutDiffType.VARIABLE_TYPE, src: srcSlotVar, cmp: cmpSlotVar, @@ -215,5 +243,64 @@ export const checkLayouts = ( } } - return _uniqWith(diffs, _isEqual); + if (checkRemovals) { + for (const byte of Object.keys(srcMapping)) { + const srcSlotVar = srcMapping[byte]; + const cmpSlotVar = cmpMapping[byte]; + + const byteIndex = BigInt(byte); + const location = { + slot: byteIndex / STORAGE_WORD_SIZE, + offset: byteIndex % STORAGE_WORD_SIZE, + }; + + if (!cmpSlotVar) + diffs.push({ + location, + type: StorageLayoutDiffType.VARIABLE_REMOVED, + src: srcSlotVar, + }); + } + } + + return _uniqWith( + _sortBy(diffs, ["location.slot", "location.offset"]), // make sure it's ordered by storage byte order + ({ location: location1, ...diff1 }, { location: location2, ...diff2 }) => _isEqual(diff1, diff2) // only keep first byte diff of a variable, which corresponds to the start byte + ).concat(address && provider ? await checkAddedStorageSlots(added, address, provider) : []); +}; + +const checkAddedStorageSlots = async ( + added: StorageLayoutDiffAdded[], + address: string, + provider: Provider +): Promise => { + const storage: { [slot: string]: string } = {}; + const diffs: StorageLayoutDiffAddedNonZeroSlot[] = []; + + for (const diff of _sortBy(added, ["location.slot", "location.offset"])) { + const slot = diff.location.slot.toString(); + + const memoized = storage[slot]; + let value = memoized ?? (await provider.getStorageAt(address, slot)); + if (!memoized) storage[slot] = value; + + const byteIndex = value.length - Number((diff.location.offset + 1n) * 2n); + value = value.substring(byteIndex, byteIndex + 2); + + if (value === "00") continue; + + diffs.push({ + ...diff, + type: StorageLayoutDiffType.NON_ZERO_ADDED_SLOT, + value, + }); + } + + return _uniqWith( + diffs, + ( + { location: location1, value: value1, ...diff1 }, + { location: location2, value: value2, ...diff2 } + ) => _isEqual(diff1, diff2) // only keep first byte diff of a variable, which corresponds to the start byte + ); }; diff --git a/src/format.ts b/src/format.ts index 9c8a6cc..f1f5bcb 100644 --- a/src/format.ts +++ b/src/format.ts @@ -23,7 +23,7 @@ export const diffTitles: { [type in StorageLayoutDiffType]?: string } = { export const formatDiff = ( cmpDef: ParsedSource, - { type, ...diff }: StorageLayoutDiff + diff: StorageLayoutDiff ): FormattedStorageLayoutDiff => { const location = (diff.parent @@ -32,7 +32,7 @@ export const formatDiff = ( `, byte #${diff.location.offset.toString()}`; const loc = cmpDef.tokens.find( - (token) => token.type === "Identifier" && token.value === diff.cmp.label + (token) => token.type === "Identifier" && "cmp" in diff && token.value === diff.cmp.label )?.loc ?? { start: { line: 0, @@ -44,6 +44,7 @@ export const formatDiff = ( }, }; + const { type } = diff; switch (type) { case StorageLayoutDiffType.LABEL: return { @@ -57,6 +58,12 @@ export const formatDiff = ( type, message: `variable "${diff.src.fullLabel}" was of type "${diff.src.typeLabel}" but is now "${diff.cmp.typeLabel}" (${location})`, }; + case StorageLayoutDiffType.VARIABLE_REMOVED: + return { + loc, + type, + message: `variable "${diff.src.fullLabel}" of type "${diff.src.typeLabel}" was removed (${location})`, + }; case StorageLayoutDiffType.TYPE_REMOVED: return { loc, @@ -75,19 +82,17 @@ export const formatDiff = ( type, message: `variable "${diff.src.fullLabel}" of type "${diff.src.typeLabel}" was replaced by variable "${diff.cmp.fullLabel}" of type "${diff.cmp.typeLabel}" (${location})`, }; + case StorageLayoutDiffType.NON_ZERO_ADDED_SLOT: + return { + loc, + type, + message: `variable "${diff.cmp.fullLabel}" of type "${diff.cmp.typeLabel}" was added at a non-zero storage byte (${location}: 0x${diff.value})`, + }; default: return { loc, type, - message: `Storage layout diff: variable "${diff.src.fullLabel}" of type "${diff.src.type}" was replaced by variable "${diff.cmp.fullLabel}" of type "${diff.cmp.type}" (${location})`, + message: `Storage layout diff`, }; } }; - -export const formatGitHubDiff = ( - cmpDef: ParsedSource, - { loc, type, message }: FormattedStorageLayoutDiff -) => - `::${diffLevels[type] || "error"} file=${cmpDef.path},line=${loc.start.line},endLine=${ - loc.end.line - },col=${loc.start.column},endColumn=${loc.end.column},title=${diffTitles[type]}::${message}`; diff --git a/src/index.ts b/src/index.ts index d0f5cdb..d23ac0e 100755 --- a/src/index.ts +++ b/src/index.ts @@ -5,15 +5,20 @@ import { dirname, resolve } from "path"; import * as artifact from "@actions/artifact"; import * as core from "@actions/core"; import { context, getOctokit } from "@actions/github"; +import { getDefaultProvider } from "@ethersproject/providers"; import { checkLayouts } from "./check"; -import { diffLevels, formatDiff, formatGitHubDiff } from "./format"; +import { diffLevels, diffTitles, formatDiff } from "./format"; import { createLayout, parseSource, parseLayout } from "./input"; +import { StorageLayoutDiffType } from "./types"; const token = process.env.GITHUB_TOKEN || core.getInput("token"); const baseBranch = core.getInput("base"); const headBranch = core.getInput("head"); const contract = core.getInput("contract"); +const address = core.getInput("address"); +const rpcUrl = core.getInput("rpcUrl"); +const failOnRemoval = core.getInput("failOnRemoval") === "true"; const contractEscaped = contract.replace(/\//g, "_").replace(/:/g, "-"); const getReportPath = (branch: string, baseName: string) => @@ -28,6 +33,8 @@ const artifactClient = artifact.create(); const { owner, repo } = context.repo; const repository = owner + "/" + repo; +const provider = getDefaultProvider(rpcUrl); + let srcContent: string; let refCommitHash: string | undefined; @@ -124,7 +131,11 @@ async function run() { core.endGroup(); core.startGroup("Check storage layout"); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout, { + address, + provider, + checkRemovals: failOnRemoval, + }); if (diffs.length > 0) { core.info(`Parse source code`); @@ -133,16 +144,33 @@ async function run() { const formattedDiffs = diffs.map((diff) => { const formattedDiff = formatDiff(cmpDef, diff); - console.log(formatGitHubDiff(cmpDef, formattedDiff)); + const title = diffTitles[formattedDiff.type]; + const level = diffLevels[formattedDiff.type] || "error"; + console.log( + core[level](formattedDiff.message, { + title, + file: cmpDef.path, + startLine: formattedDiff.loc.start.line, + endLine: formattedDiff.loc.end.line, + startColumn: formattedDiff.loc.start.column, + endColumn: formattedDiff.loc.end.column, + }) + ); return formattedDiff; }); - if (formattedDiffs.filter((diff) => diffLevels[diff.type] === "error").length > 0) + if ( + formattedDiffs.filter((diff) => diffLevels[diff.type] === "error").length > 0 || + (failOnRemoval && + formattedDiffs.filter((diff) => diff.type === StorageLayoutDiffType.VARIABLE_REMOVED) + .length > 0) + ) return core.setFailed( "Unsafe storage layout changes detected. Please see above for details." ); } + core.endGroup(); } catch (error: any) { core.setFailed(error.message); diff --git a/src/types.ts b/src/types.ts index 43c5c05..db85a96 100644 --- a/src/types.ts +++ b/src/types.ts @@ -58,9 +58,11 @@ export enum StorageLayoutDiffType { TYPE_CHANGED = "TYPE_CHANGED", VARIABLE = "VARIABLE", VARIABLE_TYPE = "VARIABLE_TYPE", + VARIABLE_REMOVED = "VARIABLE_REMOVED", + NON_ZERO_ADDED_SLOT = "NON_ZERO_ADDED_SLOT", } -export interface StorageLayoutDiffLocation { +export interface StorageLayoutLocation { slot: bigint; offset: bigint; } @@ -71,10 +73,42 @@ export interface StorageVariableDetails extends StorageVariableExact { startByte: bigint; } -export interface StorageLayoutDiff { - type: StorageLayoutDiffType; - location: StorageLayoutDiffLocation; +export interface StorageLayoutDiffBase { + type: Exclude< + StorageLayoutDiffType, + StorageLayoutDiffType.NON_ZERO_ADDED_SLOT | StorageLayoutDiffType.VARIABLE_REMOVED + >; + location: StorageLayoutLocation; + src: StorageVariableDetails; + cmp: StorageVariableDetails; + parent?: string; +} + +export interface StorageLayoutDiffAdded { + location: StorageLayoutLocation; + cmp: StorageVariableDetails; + parent?: string; +} + +export interface StorageLayoutDiffAddedNonZeroSlot extends StorageLayoutDiffAdded { + type: StorageLayoutDiffType.NON_ZERO_ADDED_SLOT; + value: string; +} + +export interface StorageLayoutDiffRemoved { + type: StorageLayoutDiffType.VARIABLE_REMOVED; + location: StorageLayoutLocation; src: StorageVariableDetails; + parent?: string; +} + +export type StorageLayoutDiff = + | StorageLayoutDiffBase + | StorageLayoutDiffAddedNonZeroSlot + | StorageLayoutDiffRemoved; + +export interface StorageLayoutLocationAdded { + location: StorageLayoutLocation; cmp: StorageVariableDetails; parent?: string; } diff --git a/tests/check.test.ts b/tests/check.test.ts index 5262bd6..ed27e10 100644 --- a/tests/check.test.ts +++ b/tests/check.test.ts @@ -8,64 +8,76 @@ describe("Storage layout checks", () => { describe("Basic storage", () => { const srcLayout = parseLayout(createLayout("tests/mocks/basic/StorageRef.sol:Storage")); - it("should not raise diff when comparing source to source", () => { - const diffs = checkLayouts(srcLayout, srcLayout); + it("should not raise diff when comparing source to source", async () => { + const diffs = await checkLayouts(srcLayout, srcLayout); expect(diffs).toHaveLength(0); }); - it("should raise label diff when renaming variable", () => { + it("should raise label diff when renaming variable", async () => { const contract = "tests/mocks/basic/StorageRenamed.sol:Storage"; const cmpDef = parseSource(contract); const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(1); expect(formatDiff(cmpDef, diffs[0]).message).toEqual( 'variable "_initializing" was renamed to "_initialization". Is it intentional? (storage slot 0x0000000000000000000000000000000000000000000000000000000000000000, byte #1)' ); }); - it("should not raise diff when variable added at start of gap slot", () => { + it("should not raise diff when variable added at start of gap slot", async () => { const contract = "tests/mocks/basic/StorageGapStart.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); - it("should not raise diff when variable added at end of gap slot", () => { + it("should not raise diff when variable added at end of gap slot", async () => { const contract = "tests/mocks/basic/StorageGapEnd.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); - it("should raise type diff when changing type of variable", () => { + it("should raise type diff when changing type of variable", async () => { const contract = "tests/mocks/basic/StorageChanged.sol:Storage"; const cmpDef = parseSource(contract); const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(1); expect(formatDiff(cmpDef, diffs[0]).message).toEqual( 'variable "_owner" was of type "address" but is now "uint192" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000c, byte #0)' ); }); - it("should not raise diff when removing variable", () => { + it("should not raise diff when removing variable and not checking removals", async () => { const contract = "tests/mocks/basic/StorageRemoved.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); - it("should not raise diff when extending storage", () => { + it("should not raise diff when removing variable and checking removals", async () => { + const contract = "tests/mocks/basic/StorageRemoved.sol:Storage"; + const cmpDef = parseSource(contract); + const cmpLayout = parseLayout(createLayout(contract)); + + const diffs = await checkLayouts(srcLayout, cmpLayout, { checkRemovals: true }); + expect(diffs).toHaveLength(1); + expect(formatDiff(cmpDef, diffs[0]).message).toEqual( + 'variable "added" of type "uint8" was removed (storage slot 0x0000000000000000000000000000000000000000000000000000000000000019, byte #0)' + ); + }); + + it("should not raise diff when extending storage", async () => { const contract = "tests/mocks/basic/StorageExtended.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); }); @@ -73,63 +85,63 @@ describe("Storage layout checks", () => { describe("Struct storage", () => { const srcLayout = parseLayout(createLayout("tests/mocks/struct/StorageStructRef.sol:Storage")); - it("should not raise diff when changing name of struct", () => { + it("should not raise diff when changing name of struct", async () => { const contract = "tests/mocks/struct/StorageStructRenamed.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); - it("should raise label diff when switching struct variables", () => { + it("should raise label diff when switching struct variables", async () => { const contract = "tests/mocks/struct/StorageStructChanged.sol:Storage"; const cmpDef = parseSource(contract); const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(4); expect(formatDiff(cmpDef, diffs[0]).message).toEqual( - 'variable "(Storage.Struct)myStruct.c" was renamed to "(Storage.Struct)myStruct.d". Is it intentional? (storage slot 0x000000000000000000000000000000000000000000000000000000000000000f, byte #0)' + 'variable "(Storage.Struct myStruct).c" was renamed to "(Storage.Struct myStruct).d". Is it intentional? (storage slot 0x000000000000000000000000000000000000000000000000000000000000000f, byte #0)' ); expect(formatDiff(cmpDef, diffs[1]).message).toEqual( - 'variable "(Storage.Struct)myStruct.d" was renamed to "(Storage.Struct)myStruct.c". Is it intentional? (storage slot 0x000000000000000000000000000000000000000000000000000000000000000f, byte #4)' + 'variable "(Storage.Struct myStruct).d" was renamed to "(Storage.Struct myStruct).c". Is it intentional? (storage slot 0x000000000000000000000000000000000000000000000000000000000000000f, byte #4)' ); expect(formatDiff(cmpDef, diffs[2]).message).toEqual( - 'variable "(Storage.Struct)structs.c" was renamed to "(Storage.Struct)structs.d". Is it intentional? (storage slot 0x60811857dd566889ff6255277d82526f2d9b3bbcb96076be22a5860765ac3d08, byte #0)' + 'variable "(Storage.Struct structs).c" was renamed to "(Storage.Struct structs).d". Is it intentional? (storage slot 0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbb, byte #0)' ); expect(formatDiff(cmpDef, diffs[3]).message).toEqual( - 'variable "(Storage.Struct)structs.d" was renamed to "(Storage.Struct)structs.c". Is it intentional? (storage slot 0x60811857dd566889ff6255277d82526f2d9b3bbcb96076be22a5860765ac3d08, byte #4)' + 'variable "(Storage.Struct structs).d" was renamed to "(Storage.Struct structs).c". Is it intentional? (storage slot 0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dbb, byte #4)' ); }); - it("should not raise diff when changing struct for smaller, free storage word", () => { + it("should not raise diff when changing struct for smaller, free storage word", async () => { const contract = "tests/mocks/struct/StorageStructSmaller.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); - it("should raise variable diff when using larger struct", () => { + it("should raise variable diff when using larger struct", async () => { const contract = "tests/mocks/struct/StorageStructLarger.sol:Storage"; const cmpDef = parseSource(contract); const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(2); expect(formatDiff(cmpDef, diffs[0]).message).toEqual( - 'variable "(Storage.Struct)myStruct.b" of type "uint256" was replaced by variable "(Storage.Struct)myStruct.e" of type "address" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000e, byte #0)' + 'variable "(Storage.Struct myStruct).b" of type "uint256" was replaced by variable "(Storage.Struct myStruct).e" of type "address" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000e, byte #0)' ); expect(formatDiff(cmpDef, diffs[1]).message).toEqual( - 'variable "(Storage.Struct)structs.b" of type "uint256" was replaced by variable "(Storage.Struct)structs.e" of type "address" (storage slot 0x60811857dd566889ff6255277d82526f2d9b3bbcb96076be22a5860765ac3d07, byte #0)' + 'variable "(Storage.Struct structs).b" of type "uint256" was replaced by variable "(Storage.Struct structs).e" of type "address" (storage slot 0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dba, byte #0)' ); }); - it("should not raise diff when extending struct", () => { + it("should not raise diff when extending struct", async () => { const contract = "tests/mocks/struct/StorageStructExtended.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); }); @@ -139,58 +151,58 @@ describe("Storage layout checks", () => { createLayout("tests/mocks/mapping/StorageMappingRef.sol:Storage") ); - it("should raise label diff when changing name of mapping", () => { + it("should raise label diff when changing name of mapping", async () => { const contract = "tests/mocks/mapping/StorageMappingRenamed.sol:Storage"; const cmpDef = parseSource(contract); const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(1); expect(formatDiff(cmpDef, diffs[0]).message).toEqual( 'variable "structOf" was renamed to "_structOf". Is it intentional? (storage slot 0x000000000000000000000000000000000000000000000000000000000000000d, byte #0)' ); }); - it("should raise type diff when changing mapping", () => { + it("should raise type diff when changing mapping", async () => { const contract = "tests/mocks/mapping/StorageMappingChanged.sol:Storage"; const cmpDef = parseSource(contract); const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(1); expect(formatDiff(cmpDef, diffs[0]).message).toEqual( - 'variable "nestedMapping[0]" was of type "mapping(address => mapping(uint256 => Storage.Struct))" but is now "mapping(uint256 => mapping(uint256 => Storage.Struct))" (storage slot 0xc13ad76448cbefd1ee83b801bcd8f33061f2577d6118395e7b44ea21c7ef62e0, byte #0)' + 'variable "nestedMapping[0]" was of type "mapping(address => mapping(uint256 => Storage.Struct))" but is now "mapping(uint256 => mapping(uint256 => Storage.Struct))" (storage slot 0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49, byte #0)' ); }); - it("should raise type diff when extending mapping", () => { + it("should raise type diff when extending mapping", async () => { const contract = "tests/mocks/mapping/StorageMappingExtended.sol:Storage"; const cmpDef = parseSource(contract); const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(2); expect(formatDiff(cmpDef, diffs[0]).message).toEqual( 'variable "structOf" was of type "mapping(address => Storage.Struct)" but is now "mapping(address => mapping(address => Storage.Struct))" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000d, byte #0)' ); expect(formatDiff(cmpDef, diffs[1]).message).toEqual( - 'variable "(Storage.Struct)structOf[0].a" of type "bool" was replaced by variable "structOf[0]" of type "mapping(address => Storage.Struct)" (storage slot 0x91d3ecd2a4b33a91a2a6360f87f530a38fc3d60e51ba8524f83e3f14addbb728, byte #0)' + 'variable "(Storage.Struct structOf[0]).a" of type "bool" was replaced by variable "structOf[0]" of type "mapping(address => Storage.Struct)" (storage slot 0x81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee, byte #0)' ); }); - it("should not raise diff when renaming mapping struct", () => { + it("should not raise diff when renaming mapping struct", async () => { const contract = "tests/mocks/mapping/StorageMappingStructRenamed.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); - it("should not raise diff when extended mapping struct", () => { + it("should not raise diff when extended mapping struct", async () => { const contract = "tests/mocks/mapping/StorageMappingStructExtended.sol:Storage"; const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); }); @@ -200,12 +212,12 @@ describe("Storage layout checks", () => { createLayout("tests/mocks/interface/StorageInterfaceRef.sol:Storage") ); - it("should not raise diff when extending interface", () => { + it("should not raise diff when extending interface", async () => { const contract = "tests/mocks/interface/StorageInterfaceExtended.sol:Storage"; const cmpDef = parseSource(contract); const cmpLayout = parseLayout(createLayout(contract)); - const diffs = checkLayouts(srcLayout, cmpLayout); + const diffs = await checkLayouts(srcLayout, cmpLayout); expect(diffs).toHaveLength(0); }); }); diff --git a/yarn.lock b/yarn.lock index d0b0f21..dc8a8a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -481,6 +481,231 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/basex@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" + integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/providers@^5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" + integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" + integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/sha2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" + integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" + integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@github/browserslist-config@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@github/browserslist-config/-/browserslist-config-1.0.0.tgz#952fe6da3e6b8ed6a368f3a1a08a9d2ef84e8d04" @@ -1560,17 +1785,22 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== before-after-hook@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== -bn.js@^5.2.0: +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -1590,6 +1820,11 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + browserslist@^4.20.2, browserslist@^4.21.0: version "4.21.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf" @@ -1619,14 +1854,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -1851,6 +2078,19 @@ electron-to-chromium@^1.4.188: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.189.tgz#4e5b221dc44e09e9dddc9abbc6457857dee7ba25" integrity sha512-dQ6Zn4ll2NofGtxPXaDfY2laIa6NyCQdqXYHdwH90GJQW0LpJJib0ZU/ERtbb0XkBEmUD2eJtagbOie3pdMiPg== +elliptic@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emittery@^0.10.2: version "0.10.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" @@ -2493,6 +2733,23 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -2503,11 +2760,6 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - ignore@^5.0.5, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" @@ -2542,7 +2794,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3100,6 +3352,11 @@ js-sdsl@^4.1.4: resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" integrity sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q== +js-sha3@0.8.0, js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3160,24 +3417,6 @@ jsx-ast-utils@^3.3.2: array-includes "^3.1.5" object.assign "^4.1.2" -keccak256@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58" - integrity sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw== - dependencies: - bn.js "^5.2.0" - buffer "^6.0.3" - keccak "^3.0.2" - -keccak@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" - integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== - dependencies: - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - readable-stream "^3.6.0" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -3326,6 +3565,16 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -3368,11 +3617,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -3380,11 +3624,6 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-gyp-build@^4.2.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -3647,15 +3886,6 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" @@ -3735,11 +3965,6 @@ safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - semver@7.x, semver@^7.3.5, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" @@ -3863,13 +4088,6 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -4092,11 +4310,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" @@ -4176,6 +4389,11 @@ write-file-atomic@^4.0.1: imurmurhash "^0.1.4" signal-exit "^3.0.7" +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"