-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add proxy chain support to gas stats output #8119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
c07ede4
feat: add subtitle support to formatTable
schaable 1d2652a
feat: support proxy chain grouping in gas stats
schaable e9f91f3
test: add proxy contract examples to example-project
schaable c8235d7
refactor: move gas analytics helpers from gas-analytics-manager to he…
schaable ff5a97e
add changeset
schaable b39d56f
fix lint
schaable 1e20d2c
refactor: split gas-analytics helpers.ts into helpers/ directory to b…
schaable d6f3ce1
fix: rename test suite from Counter to Proxies in Proxies.ts
schaable a69ac55
fix: rename CounterTest to ProxiesTest in Proxies.t.sol
schaable fadaab3
docs: clarify why Proxy2 keeps an implementation slot
schaable File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@nomicfoundation/hardhat-utils": patch | ||
| "hardhat": patch | ||
| --- | ||
|
|
||
| Show proxy chain information in --gas-stats and --gas-stats-json output |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {console} from "hardhat/console.sol"; | ||
|
|
||
| // We need to define two different proxies so that their implementation | ||
| // storage slots are different, so we can chain them. | ||
| abstract contract BaseProxy { | ||
| fallback() external payable { | ||
| address impl = getImplementation(); | ||
| assembly { | ||
| calldatacopy(0, 0, calldatasize()) | ||
| let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0) | ||
| returndatacopy(0, 0, returndatasize()) | ||
| switch result | ||
| case 0 { | ||
| revert(0, returndatasize()) | ||
| } | ||
| default { | ||
| return(0, returndatasize()) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function getImplementation() internal view virtual returns (address); | ||
|
|
||
| receive() external payable {} | ||
| } | ||
|
|
||
| contract Proxy is BaseProxy { | ||
| address implementation; | ||
|
|
||
| constructor(address _impl) { | ||
| // console.log("Setting implementation for Proxy:", _impl); | ||
| implementation = _impl; | ||
| } | ||
|
|
||
| function getImplementation() internal view override returns (address) { | ||
| // console.log("Getting implementation for Proxy:", implementation); | ||
| return implementation; | ||
| } | ||
| } | ||
|
|
||
| contract Proxy2 is BaseProxy { | ||
| // `implementation` occupies slot 0 so that when Proxy's code runs via | ||
| // delegatecall in Proxy2's storage context, reading slot 0 returns the | ||
| // actual implementation address. | ||
| address implementation; | ||
| address proxy1; | ||
|
|
||
| constructor(address _impl, address _proxy1) { | ||
| // console.log("Setting implementation for Proxy2:", _impl); | ||
| proxy1 = _proxy1; | ||
| implementation = _impl; | ||
| } | ||
|
|
||
| function getImplementation() internal view override returns (address) { | ||
| // console.log("Getting implementation for Proxy2:", proxy1); | ||
| return proxy1; | ||
| } | ||
| } | ||
|
|
||
| contract Impl1 { | ||
| function one() external returns (uint256) { | ||
| return 1; | ||
| } | ||
| } | ||
|
|
||
| contract Impl2 { | ||
| function two() external returns (uint256) { | ||
| return 2; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {Test} from "forge-std/Test.sol"; | ||
| import "../../contracts/Proxies.sol"; | ||
|
|
||
| contract ProxiesTest is Test { | ||
| function test_ShouldTrackProxiedCallsToImpl1AndImpl2AsSeparate() public { | ||
| Impl1 impl1 = new Impl1(); | ||
| Impl2 impl2 = new Impl2(); | ||
|
|
||
| Proxy proxy1 = new Proxy(address(impl1)); | ||
| Proxy proxy2 = new Proxy(address(impl2)); | ||
|
|
||
| Impl1 i1 = Impl1(address(proxy1)); | ||
| Impl2 i2 = Impl2(address(proxy2)); | ||
|
|
||
| // emit log("Calling Proxy -> Impl1"); | ||
| i1.one(); | ||
|
|
||
| // emit log("Calling Proxy -> Impl2"); | ||
| i2.two(); | ||
|
|
||
| // emit log("Calling Impl1"); | ||
| impl1.one(); | ||
| } | ||
|
|
||
| function test_ShouldTrackProxiedCallsToImpl1AsSeparateWithDifferentProxyChains() | ||
| public | ||
| { | ||
| // We use the same impl but different proxy chains | ||
| Impl1 impl1 = new Impl1(); | ||
|
|
||
| Proxy proxy1 = new Proxy(address(impl1)); | ||
| Proxy proxy2 = new Proxy(address(impl1)); | ||
|
|
||
| // We use a proxy in front of proxy1 | ||
| Proxy2 proxy11 = new Proxy2(address(impl1), address(proxy1)); | ||
|
|
||
| Impl1 i1 = Impl1(address(proxy11)); | ||
| Impl1 i2 = Impl1(address(proxy2)); | ||
|
|
||
| // emit log("Calling Proxy2 -> Proxy -> Impl1"); | ||
| i1.one(); | ||
|
|
||
| // emit log("Calling Proxy1 -> Impl1"); | ||
| i2.one(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { describe, it } from "node:test"; | ||
|
|
||
| import { network } from "hardhat"; | ||
|
|
||
| describe("Proxies", async function () { | ||
| const { viem } = await network.connect(); | ||
|
|
||
| it("Should track the proxied calls to Impl1 and Impl2 as separate, despite using the same proxy", async function () { | ||
| const impl1 = await viem.deployContract("Impl1"); | ||
| const impl2 = await viem.deployContract("Impl2"); | ||
|
|
||
| const proxy1 = await viem.deployContract("Proxy", [impl1.address]); | ||
| const proxy2 = await viem.deployContract("Proxy", [impl2.address]); | ||
|
|
||
| const i1 = await viem.getContractAt("Impl1", proxy1.address); | ||
| const i2 = await viem.getContractAt("Impl2", proxy2.address); | ||
|
|
||
| // console.log("Calling Proxy -> Impl1"); | ||
| await i1.write.one(); | ||
|
|
||
| // console.log("Calling Proxy -> Impl2"); | ||
| await i2.write.two(); | ||
|
|
||
| // console.log("Calling Impl1"); | ||
| await impl1.write.one(); | ||
| }); | ||
|
|
||
| it("Should track the proxied calls to Impl1 as separate if they use separate proxy chains", async function () { | ||
| // We use the same impl but different proxy chains | ||
| const impl1 = await viem.deployContract("Impl1"); | ||
|
|
||
| const proxy1 = await viem.deployContract("Proxy", [impl1.address]); | ||
| const proxy2 = await viem.deployContract("Proxy", [impl1.address]); | ||
|
|
||
| // We use a proxy in front of Proxy1 | ||
| const proxy11 = await viem.deployContract("Proxy2", [ | ||
| impl1.address, | ||
| proxy1.address, | ||
| ]); | ||
|
|
||
| const i1 = await viem.getContractAt("Impl1", proxy11.address); | ||
| const i2 = await viem.getContractAt("Impl1", proxy2.address); | ||
|
|
||
| // console.log("Calling Proxy2 -> Proxy -> Impl1"); | ||
| await i1.write.one(); | ||
| // console.log("Calling Proxy1 -> Impl1"); | ||
| await i2.write.one(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.