Skip to content

Commit e2e9908

Browse files
Amxxernestognw
andauthored
Add guidelines for numeric literals (#5936)
Co-authored-by: ernestognw <[email protected]>
1 parent 49882a3 commit e2e9908

File tree

14 files changed

+89
-74
lines changed

14 files changed

+89
-74
lines changed

GUIDELINES.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ In addition to the official Solidity Style Guide we have a number of other conve
117117

118118
Some standards (e.g. ERC-20) use present tense, and in those cases the
119119
standard specification is used.
120-
120+
121121
* Interface names should have a capital I prefix.
122122

123123
```solidity
@@ -141,7 +141,7 @@ In addition to the official Solidity Style Guide we have a number of other conve
141141
* Unchecked arithmetic blocks should contain comments explaining why overflow is guaranteed not to happen. If the reason is immediately apparent from the line above the unchecked block, the comment may be omitted.
142142

143143
* Custom errors should be declared following the [EIP-6093](https://eips.ethereum.org/EIPS/eip-6093) rationale whenever reasonable. Also, consider the following:
144-
144+
145145
* The domain prefix should be picked in the following order:
146146
1. Use `ERC<number>` if the error is a violation of an ERC specification.
147147
2. Use the name of the underlying component where it belongs (eg. `Governor`, `ECDSA`, or `Timelock`).
@@ -153,3 +153,18 @@ In addition to the official Solidity Style Guide we have a number of other conve
153153
4. Declare the error in an extension if the error only happens in such extension or child contracts.
154154

155155
* Custom error names should not be declared twice along the library to avoid duplicated identifier declarations when inheriting from multiple contracts.
156+
157+
* Numeric literals should use appropriate formats based on their purpose to improve code readability:
158+
159+
**Memory-related operations (use hexadecimal):**
160+
* Memory locations: `mload(64)``mload(0x40)`
161+
* Memory offsets: `mstore(add(ptr, 32), value)``mstore(add(ptr, 0x20), value)`
162+
* Memory lengths: `keccak256(ptr, 85)``keccak256(ptr, 0x55)`
163+
164+
**Bit operations (use decimal):**
165+
* Shift amounts: `shl(0x80, value)``shl(128, value)`
166+
* Bit masks and positions should use decimal when representing bit counts
167+
168+
**Exceptions:**
169+
* Trivially small values (1, 2) may use decimal even in memory operations: `ptr := add(ptr, 1)`
170+
* In `call`/`staticcall`/`delegatecall`, decimal zero is acceptable when both location and length are zero

contracts/account/utils/draft-ERC7579Utils.sol

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ library ERC7579Utils {
126126
Mode mode
127127
) internal pure returns (CallType callType, ExecType execType, ModeSelector selector, ModePayload payload) {
128128
return (
129-
CallType.wrap(Packing.extract_32_1(Mode.unwrap(mode), 0)),
130-
ExecType.wrap(Packing.extract_32_1(Mode.unwrap(mode), 1)),
131-
ModeSelector.wrap(Packing.extract_32_4(Mode.unwrap(mode), 6)),
132-
ModePayload.wrap(Packing.extract_32_22(Mode.unwrap(mode), 10))
129+
CallType.wrap(Packing.extract_32_1(Mode.unwrap(mode), 0x00)),
130+
ExecType.wrap(Packing.extract_32_1(Mode.unwrap(mode), 0x01)),
131+
ModeSelector.wrap(Packing.extract_32_4(Mode.unwrap(mode), 0x06)),
132+
ModePayload.wrap(Packing.extract_32_22(Mode.unwrap(mode), 0x0a))
133133
);
134134
}
135135

@@ -146,9 +146,9 @@ library ERC7579Utils {
146146
function decodeSingle(
147147
bytes calldata executionCalldata
148148
) internal pure returns (address target, uint256 value, bytes calldata callData) {
149-
target = address(bytes20(executionCalldata[0:20]));
150-
value = uint256(bytes32(executionCalldata[20:52]));
151-
callData = executionCalldata[52:];
149+
target = address(bytes20(executionCalldata[0x00:0x14]));
150+
value = uint256(bytes32(executionCalldata[0x14:0x34]));
151+
callData = executionCalldata[0x34:];
152152
}
153153

154154
/// @dev Encodes a delegate call execution. See {decodeDelegate}.
@@ -163,8 +163,8 @@ library ERC7579Utils {
163163
function decodeDelegate(
164164
bytes calldata executionCalldata
165165
) internal pure returns (address target, bytes calldata callData) {
166-
target = address(bytes20(executionCalldata[0:20]));
167-
callData = executionCalldata[20:];
166+
target = address(bytes20(executionCalldata[0:0x14]));
167+
callData = executionCalldata[0x14:];
168168
}
169169

170170
/// @dev Encodes a batch of executions. See {decodeBatch}.
@@ -180,17 +180,17 @@ library ERC7579Utils {
180180
uint256 bufferLength = executionCalldata.length;
181181

182182
// Check executionCalldata is not empty.
183-
if (bufferLength < 32) revert ERC7579DecodingError();
183+
if (bufferLength < 0x20) revert ERC7579DecodingError();
184184

185185
// Get the offset of the array (pointer to the array length).
186-
uint256 arrayLengthOffset = uint256(bytes32(executionCalldata[0:32]));
186+
uint256 arrayLengthOffset = uint256(bytes32(executionCalldata[0x00:0x20]));
187187

188188
// The array length (at arrayLengthOffset) should be 32 bytes long. We check that this is within the
189189
// buffer bounds. Since we know bufferLength is at least 32, we can subtract with no overflow risk.
190-
if (arrayLengthOffset > bufferLength - 32) revert ERC7579DecodingError();
190+
if (arrayLengthOffset > bufferLength - 0x20) revert ERC7579DecodingError();
191191

192192
// Get the array length. arrayLengthOffset + 32 is bounded by bufferLength so it does not overflow.
193-
uint256 arrayLength = uint256(bytes32(executionCalldata[arrayLengthOffset:arrayLengthOffset + 32]));
193+
uint256 arrayLength = uint256(bytes32(executionCalldata[arrayLengthOffset:arrayLengthOffset + 0x20]));
194194

195195
// Check that the buffer is long enough to store the array elements as "offset pointer":
196196
// - each element of the array is an "offset pointer" to the data.
@@ -200,7 +200,7 @@ library ERC7579Utils {
200200
//
201201
// Since we know bufferLength is at least arrayLengthOffset + 32, we can subtract with no overflow risk.
202202
// Solidity limits length of such arrays to 2**64-1, this guarantees `arrayLength * 32` does not overflow.
203-
if (arrayLength > type(uint64).max || bufferLength - arrayLengthOffset - 32 < arrayLength * 32)
203+
if (arrayLength > type(uint64).max || bufferLength - arrayLengthOffset - 0x20 < arrayLength * 0x20)
204204
revert ERC7579DecodingError();
205205

206206
assembly ("memory-safe") {

contracts/metatx/ERC2771Forwarder.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ contract ERC2771Forwarder is EIP712, Nonces {
287287
uint256 gasLeft;
288288

289289
assembly ("memory-safe") {
290-
success := call(reqGas, to, value, add(data, 0x20), mload(data), 0, 0)
290+
success := call(reqGas, to, value, add(data, 0x20), mload(data), 0x00, 0x00)
291291
gasLeft := gas()
292292
}
293293

@@ -318,9 +318,9 @@ contract ERC2771Forwarder is EIP712, Nonces {
318318
// |-----------|----------|--------------------------------------------------------------------|
319319
// | | | result ↓ |
320320
// | 0x00:0x1F | selector | 0x0000000000000000000000000000000000000000000000000000000000000001 |
321-
success := staticcall(gas(), target, add(encodedParams, 0x20), mload(encodedParams), 0, 0x20)
321+
success := staticcall(gas(), target, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
322322
returnSize := returndatasize()
323-
returnValue := mload(0)
323+
returnValue := mload(0x00)
324324
}
325325

326326
return success && returnSize >= 0x20 && returnValue > 0;

contracts/proxy/Clones.sol

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ library Clones {
5151
assembly ("memory-safe") {
5252
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
5353
// of the `implementation` address with the bytecode before the address.
54-
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
54+
mstore(0x00, or(shr(232, shl(96, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
5555
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
56-
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
56+
mstore(0x20, or(shl(120, implementation), 0x5af43d82803e903d91602b57fd5bf3))
5757
instance := create(value, 0x09, 0x37)
5858
}
5959
if (instance == address(0)) {
@@ -98,9 +98,9 @@ library Clones {
9898
assembly ("memory-safe") {
9999
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
100100
// of the `implementation` address with the bytecode before the address.
101-
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
101+
mstore(0x00, or(shr(232, shl(96, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
102102
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
103-
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
103+
mstore(0x20, or(shl(120, implementation), 0x5af43d82803e903d91602b57fd5bf3))
104104
instance := create2(value, 0x09, 0x37, salt)
105105
}
106106
if (instance == address(0)) {
@@ -259,9 +259,9 @@ library Clones {
259259
* function should only be used to check addresses that are known to be clones.
260260
*/
261261
function fetchCloneArgs(address instance) internal view returns (bytes memory) {
262-
bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
262+
bytes memory result = new bytes(instance.code.length - 0x2d); // revert if length is too short
263263
assembly ("memory-safe") {
264-
extcodecopy(instance, add(result, 32), 45, mload(result))
264+
extcodecopy(instance, add(result, 0x20), 0x2d, mload(result))
265265
}
266266
return result;
267267
}
@@ -280,11 +280,11 @@ library Clones {
280280
address implementation,
281281
bytes memory args
282282
) private pure returns (bytes memory) {
283-
if (args.length > 24531) revert CloneArgumentsTooLong();
283+
if (args.length > 0x5fd3) revert CloneArgumentsTooLong();
284284
return
285285
abi.encodePacked(
286286
hex"61",
287-
uint16(args.length + 45),
287+
uint16(args.length + 0x2d),
288288
hex"3d81600a3d39f3363d3d373d3d3d363d73",
289289
implementation,
290290
hex"5af43d82803e903d91602b57fd5bf3",

contracts/proxy/Proxy.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,22 @@ abstract contract Proxy {
2424
// Copy msg.data. We take full control of memory in this inline assembly
2525
// block because it will not return to Solidity code. We overwrite the
2626
// Solidity scratch pad at memory position 0.
27-
calldatacopy(0, 0, calldatasize())
27+
calldatacopy(0x00, 0x00, calldatasize())
2828

2929
// Call the implementation.
3030
// out and outsize are 0 because we don't know the size yet.
31-
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
31+
let result := delegatecall(gas(), implementation, 0x00, calldatasize(), 0x00, 0x00)
3232

3333
// Copy the returned data.
34-
returndatacopy(0, 0, returndatasize())
34+
returndatacopy(0x00, 0x00, returndatasize())
3535

3636
switch result
3737
// delegatecall returns 0 on error.
3838
case 0 {
39-
revert(0, returndatasize())
39+
revert(0x00, returndatasize())
4040
}
4141
default {
42-
return(0, returndatasize())
42+
return(0x00, returndatasize())
4343
}
4444
}
4545
}

contracts/token/ERC20/utils/SafeERC20.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,13 @@ library SafeERC20 {
181181
mstore(0x00, selector)
182182
mstore(0x04, and(to, shr(96, not(0))))
183183
mstore(0x24, value)
184-
success := call(gas(), token, 0, 0, 0x44, 0, 0x20)
184+
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
185185
// if call success and return is true, all is good.
186186
// otherwise (not success or return is not true), we need to perform further checks
187187
if iszero(and(success, eq(mload(0x00), 1))) {
188188
// if the call was a failure and bubble is enabled, bubble the error
189189
if and(iszero(success), bubble) {
190-
returndatacopy(fmp, 0, returndatasize())
190+
returndatacopy(fmp, 0x00, returndatasize())
191191
revert(fmp, returndatasize())
192192
}
193193
// if the return value is not true, then the call is only successful if:
@@ -224,13 +224,13 @@ library SafeERC20 {
224224
mstore(0x04, and(from, shr(96, not(0))))
225225
mstore(0x24, and(to, shr(96, not(0))))
226226
mstore(0x44, value)
227-
success := call(gas(), token, 0, 0, 0x64, 0, 0x20)
227+
success := call(gas(), token, 0, 0x00, 0x64, 0x00, 0x20)
228228
// if call success and return is true, all is good.
229229
// otherwise (not success or return is not true), we need to perform further checks
230230
if iszero(and(success, eq(mload(0x00), 1))) {
231231
// if the call was a failure and bubble is enabled, bubble the error
232232
if and(iszero(success), bubble) {
233-
returndatacopy(fmp, 0, returndatasize())
233+
returndatacopy(fmp, 0x00, returndatasize())
234234
revert(fmp, returndatasize())
235235
}
236236
// if the return value is not true, then the call is only successful if:
@@ -260,13 +260,13 @@ library SafeERC20 {
260260
mstore(0x00, selector)
261261
mstore(0x04, and(spender, shr(96, not(0))))
262262
mstore(0x24, value)
263-
success := call(gas(), token, 0, 0, 0x44, 0, 0x20)
263+
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
264264
// if call success and return is true, all is good.
265265
// otherwise (not success or return is not true), we need to perform further checks
266266
if iszero(and(success, eq(mload(0x00), 1))) {
267267
// if the call was a failure and bubble is enabled, bubble the error
268268
if and(iszero(success), bubble) {
269-
returndatacopy(fmp, 0, returndatasize())
269+
returndatacopy(fmp, 0x00, returndatasize())
270270
revert(fmp, returndatasize())
271271
}
272272
// if the return value is not true, then the call is only successful if:

contracts/utils/Bytes.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ library Bytes {
223223
* if the buffer is all zeros.
224224
*/
225225
function clz(bytes memory buffer) internal pure returns (uint256) {
226-
for (uint256 i = 0; i < buffer.length; i += 32) {
226+
for (uint256 i = 0; i < buffer.length; i += 0x20) {
227227
bytes32 chunk = _unsafeReadBytesOffset(buffer, i);
228228
if (chunk != bytes32(0)) {
229229
return Math.min(8 * i + Math.clz(uint256(chunk)), 8 * buffer.length);

contracts/utils/Create2.sol

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,22 @@ library Create2 {
7070
assembly ("memory-safe") {
7171
let ptr := mload(0x40) // Get free memory pointer
7272

73-
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
74-
// |-------------------|---------------------------------------------------------------------------|
75-
// | bytecodeHash | CCCCCCCCCCCCC...CC |
76-
// | salt | BBBBBBBBBBBBB...BB |
77-
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
78-
// | 0xFF | FF |
79-
// |-------------------|---------------------------------------------------------------------------|
80-
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
81-
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
73+
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
74+
// |---------------------|---------------------------------------------------------------------------|
75+
// | bytecodeHash | CCCCCCCCCCCCC...CC |
76+
// | salt | BBBBBBBBBBBBB...BB |
77+
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
78+
// | 0xFF | FF |
79+
// |---------------------|---------------------------------------------------------------------------|
80+
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
81+
// | keccak(start, 0x55) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
8282

8383
mstore(add(ptr, 0x40), bytecodeHash)
8484
mstore(add(ptr, 0x20), salt)
8585
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
8686
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
8787
mstore8(start, 0xff)
88-
addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
88+
addr := and(keccak256(start, 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
8989
}
9090
}
9191
}

0 commit comments

Comments
 (0)