-
Notifications
You must be signed in to change notification settings - Fork 17
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
Add support for EIP-7201 namespaces #127
Conversation
New and removed dependencies detected. Learn more about Socket for GitHub ↗︎
🚮 Removed packages: solidity-ast@0.4.49 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Starting to review. Here is a first round of comments.
const node = this.resolveNode('*', id); | ||
if (isNodeType(nodeType, node)) { | ||
return node; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love to understand this change.
My understanding so far is that if node id
is not of type nodeType
, we don't go in the if, and we implicitely return nothing (undefined) ?
Before the change, I guess resolveNode
would fail and that would be catched by ASTDereferencerError
?
Can you explain why this change does / why its needed ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest this change ended up not being necessary, so we could revert it, but there is an explanation behind it.
What happened was that when I first introduced the namespace logic the transpiler slowed down significantly. To transpile OZ Contracts took perhaps 1 minute. So in debugging I traced it down to resolveContract
/tryResolveNode
and experimented with approaches to improve this.
The issue was that deref: ASTDereferencer
, used by tryResolveNode
, had an optimized path that worked only when the requested node type was correct. But tryResolveNode
was being used through resolveContract
to check if a node id corresponded to a ContractDefinition. If the id was instead something like a StructDefinition, deref
would fall back to brute force enumeration of all nodes in the source code to try to find one with this id (!).
The reason I introduced the '*'
feature in ASTDerefencer was to avoid falling back to the slow path, instead quickly returning whatever node was found even if it was not the wanted node type.
This helped somewhat but I ended up finding a much better optimization that causes this to not really matter. (That should also make a lot of other things faster, including hardhat-upgrades and hardhat-exposed.)
@@ -11,7 +11,7 @@ import { findAlreadyInitializable } from './find-already-initializable'; | |||
|
|||
async function getPaths() { | |||
const hardhat = require.resolve('hardhat', { paths: [process.cwd()] }); | |||
const hre: HardhatRuntimeEnvironment = await import(hardhat); | |||
const hre: HardhatRuntimeEnvironment = (await import(hardhat)).default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious if there is a way to make the type explicit in something like
const hre: HardhatRuntimeEnvironment = (await import(hardhat)).default; | |
const { default: hre } = await import(hardhat); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes but it's more verbose. It would be like this:
const { default: hre }: { default: HardhatRuntimeEnvironment } = await import(hardhat);
src/transform-namespaces.test.ts.md
Outdated
function __C2_init() internal onlyInitializing {␊ | ||
__C2_init_unchained();␊ | ||
}␊ | ||
␊ | ||
function __C2_init_unchained() internal onlyInitializing {␊ | ||
C2Storage storage $ = _getC2Storage();␊ | ||
$.s1 = "";␊ | ||
}␊ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we want the __XXX_init
and __XXX_init_unchained
to be at the top like this ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are added at the top because there is no constructor in this case, but I suppose it would be nicer if this was added right before the first function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in the latest commit.
struct C2Storage {␊ | ||
// a␊ | ||
uint x;␊ | ||
uint z;␊ | ||
␊ | ||
string s1;␊ | ||
}␊ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spacing is a bit strange.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spacing here is inherited from the original code, where variables look like this:
uint constant C = 3;
// a
uint x;
// b
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint immutable y = 2;
uint private z;
string private s1 = "";
So I think it's good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It really comes down to what // a
and // b
means and refer to. I find it strange that // a
is attached to x, and // b
is attached to y
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that may not be an issue if variables are sorted (first constants, then immutable, then private)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My reasoning was that if a comment is immediately before a variable, it should be attached to that variable. But I didn't consider the case where there is a newline between comment and variable, in that case I would say they should be considered separate and the comment should possibly be left outside of the struct...
// atached comment
uint y;
// separate comment
uint x;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're very limited in how we can handle comments because they're not present in the AST so I would leave the current behavior, unless it's producing results that we don't like for the Contracts codebase.
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
I realized none of the existing tests are passing the output through the compiler. We rely on OpenZeppelin Contracts tests for that. It makes sense to add compilation tests in this repo but we should do this in a follow up PR. |
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.C3")) - 1)) & ~bytes32(uint256(0xff))␊ | ||
bytes32 private constant C3StorageLocation = 0xaa7e0685867d809c517036b8f21e99d58bd04b1da2b202f167c355fdf82b4a00;␊ | ||
␊ | ||
function _getC3Storage() private pure returns (C3Storage storage $) {␊ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@frangio could we make the slot getters internal virtual (e.g. function _getC3Storage() internal pure virtual returns (C3Storage storage $)
)? It may be possible for contracts already deployed to specify their existing storage location this way, allowing us to upgrade to the latest OZ. It would be risky of course, in case something else about the storage had changed, and would require a lot of testing - but may give us a path to stay on the latest OZ libraries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be possible for contracts already deployed to specify their existing storage location this way
It's not possible, we've changed many things about the storage variables. We don't want to encourage this because it's guaranteed to break. For now, if you want to take this risk, feel free to fork the code and change the storage locations.
No description provided.