Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/governance/Governor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ abstract contract Governor is Context, ERC165, EIP712, Nonces, IGovernor, IERC72
* in a governance proposal to recover tokens or Ether that was sent to the governor contract by mistake.
* Note that if the executor is simply the governor itself, use of `relay` is redundant.
*/
function relay(address target, uint256 value, bytes calldata data) external payable virtual onlyGovernance {
function relay(address target, uint256 value, bytes calldata data) public payable virtual onlyGovernance {
(bool success, bytes memory returndata) = target.call{value: value}(data);
Address.verifyCallResult(success, returndata);
}
Expand Down
8 changes: 4 additions & 4 deletions contracts/governance/TimelockController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract TimelockController is AccessControl, ERC721Holder, ERC1155Holder {
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE");
uint256 internal constant _DONE_TIMESTAMP = uint256(1);
uint256 internal constant DONE_TIMESTAMP = uint256(1);

mapping(bytes32 id => uint256) private _timestamps;
uint256 private _minDelay;
Expand Down Expand Up @@ -207,7 +207,7 @@ contract TimelockController is AccessControl, ERC721Holder, ERC1155Holder {
uint256 timestamp = getTimestamp(id);
if (timestamp == 0) {
return OperationState.Unset;
} else if (timestamp == _DONE_TIMESTAMP) {
} else if (timestamp == DONE_TIMESTAMP) {
return OperationState.Done;
} else if (timestamp > block.timestamp) {
return OperationState.Waiting;
Expand Down Expand Up @@ -432,7 +432,7 @@ contract TimelockController is AccessControl, ERC721Holder, ERC1155Holder {
if (!isOperationReady(id)) {
revert TimelockUnexpectedOperationState(id, _encodeStateBitmap(OperationState.Ready));
}
_timestamps[id] = _DONE_TIMESTAMP;
_timestamps[id] = DONE_TIMESTAMP;
}

/**
Expand All @@ -445,7 +445,7 @@ contract TimelockController is AccessControl, ERC721Holder, ERC1155Holder {
* - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
*/
function updateDelay(uint256 newDelay) external virtual {
function updateDelay(uint256 newDelay) public virtual {
address sender = _msgSender();
if (sender != address(this)) {
revert TimelockUnauthorizedCaller(sender);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ abstract contract GovernorTimelockCompound is Governor {
/**
* @dev Accept admin right over the timelock.
*/
// solhint-disable-next-line private-vars-leading-underscore
// solhint-disable-next-line openzeppelin/leading-underscore
function __acceptAdmin() public {
_timelock.acceptAdmin();
}
Expand All @@ -154,7 +154,7 @@ abstract contract GovernorTimelockCompound is Governor {

* CAUTION: It is not recommended to change the timelock while there are other queued governance proposals.
*/
function updateTimelock(ICompoundTimelock newTimelock) external virtual onlyGovernance {
function updateTimelock(ICompoundTimelock newTimelock) public virtual onlyGovernance {
_updateTimelock(newTimelock);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ abstract contract GovernorTimelockControl is Governor {
*
* CAUTION: It is not recommended to change the timelock while there are other queued governance proposals.
*/
function updateTimelock(TimelockController newTimelock) external virtual onlyGovernance {
function updateTimelock(TimelockController newTimelock) public virtual onlyGovernance {
_updateTimelock(newTimelock);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
* - Must be called through a governance proposal.
* - New numerator must be smaller or equal to the denominator.
*/
function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual onlyGovernance {
function updateQuorumNumerator(uint256 newQuorumNumerator) public virtual onlyGovernance {
_updateQuorumNumerator(newQuorumNumerator);
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/metatx/ERC2771Forwarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ contract ERC2771Forwarder is EIP712, Nonces {
bytes signature;
}

bytes32 internal constant _FORWARD_REQUEST_TYPEHASH =
bytes32 internal constant FORWARD_REQUEST_TYPEHASH =
keccak256(
"ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,uint48 deadline,bytes data)"
);
Expand Down Expand Up @@ -222,7 +222,7 @@ contract ERC2771Forwarder is EIP712, Nonces {
(address recovered, ECDSA.RecoverError err, ) = _hashTypedDataV4(
keccak256(
abi.encode(
_FORWARD_REQUEST_TYPEHASH,
FORWARD_REQUEST_TYPEHASH,
request.from,
request.to,
request.value,
Expand Down
2 changes: 1 addition & 1 deletion contracts/mocks/proxy/UUPSUpgradeableMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ contract UUPSUpgradeableUnsafeMock is UUPSUpgradeableMock {
}

contract UUPSUnsupportedProxiableUUID is UUPSUpgradeableMock {
function proxiableUUID() external pure override returns (bytes32) {
function proxiableUUID() public pure override returns (bytes32) {
return keccak256("invalid UUID");
}
}
2 changes: 1 addition & 1 deletion contracts/proxy/utils/UUPSUpgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ abstract contract UUPSUpgradeable is IERC1822Proxiable {
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
function proxiableUUID() public view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/token/ERC20/extensions/ERC20Permit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces {

/// @inheritdoc IERC20Permit
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return _domainSeparatorV4();
}
}
8 changes: 4 additions & 4 deletions contracts/utils/Base64.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@ library Base64 {
* @dev Base64 Encoding/Decoding Table
* See sections 4 and 5 of https://datatracker.ietf.org/doc/html/rfc4648
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string internal constant _TABLE_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
string internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string internal constant TABLE_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
return _encode(data, _TABLE, true);
return _encode(data, TABLE, true);
}

/**
* @dev Converts a `bytes` to its Bytes64Url `string` representation.
* Output is not padded with `=` as specified in https://www.rfc-editor.org/rfc/rfc4648[rfc4648].
*/
function encodeURL(bytes memory data) internal pure returns (string memory) {
return _encode(data, _TABLE_URL, false);
return _encode(data, TABLE_URL, false);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/Multicall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ abstract contract Multicall is Context {
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
function multicall(bytes[] calldata data) public virtual returns (bytes[] memory results) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly, this is purposely external since it doesn't have an internal use. Users could just call the functions they want directly

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default, all public facing functions should be public so that overrides can call super.

If we have exceptions, the questions we need to answer are:

  1. is there any reason we think overrides won't need to call super?
  2. is there any risks in making that internally callable?

I'm not sure what override we would want to do, but I'd say calling super would be reasonnable here ... so really the question that remains is 2.

If function A internally calls multicall(...) (instead of doing this.multicall(...)) that would allow arbitrary function execution with the same caller as A's initial context. That is also what we would achieve by directly calling the corresponding function (assuming they are public and not external).

But yes, let review all the external->public changes, and we can disable the rule selectivelly if we have a good reason to keep the functions externals

bytes memory context = msg.sender == _msgSender()
? new bytes(0)
: msg.data[msg.data.length - _contextSuffixLength():];
Expand Down
94 changes: 61 additions & 33 deletions scripts/solhint-custom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ class Base {
}
}

error(node, message) {
if (!this.ignored) {
require(condition, node, message) {
if (!condition && !this.ignored) {
this.reporter.error(node, this.ruleId, message);
}
}
}

module.exports = [
class extends Base {
static ruleId = 'interface-names';
static ruleId = 'interface-only-external-functions';

ContractDefinition(node) {
if (node.kind === 'interface' && !/^I[A-Z]/.test(node.name)) {
this.error(node, 'Interface names should have a capital I prefix');
FunctionDefinition(node) {
if (node.parent.kind === 'interface') {
this.require(node.visibility === 'external', node, 'Interface functions must be external');
}
}
},
Expand All @@ -36,9 +36,12 @@ module.exports = [
static ruleId = 'private-variables';

VariableDeclaration(node) {
const constantOrImmutable = node.isDeclaredConst || node.isImmutable;
if (node.isStateVar && !constantOrImmutable && node.visibility !== 'private') {
this.error(node, 'State variables must be private');
if (node.isStateVar) {
this.require(
node.isDeclaredConst || node.isImmutable || node.visibility === 'private',
node,
'State variables must be private',
);
}
}
},
Expand All @@ -47,38 +50,63 @@ module.exports = [
static ruleId = 'leading-underscore';

VariableDeclaration(node) {
// TODO: do we want that rule ? Should no immutable variable have a prefix regardless of visibility ?
//
// else if (node.isImmutable) {
// this.require(!node.name.startsWith('_'), node, 'Immutable variables should not have leading underscore');
// }
if (node.isDeclaredConst) {
// TODO: expand visibility and fix
if (node.visibility === 'private' && /^_/.test(node.name)) {
this.error(node, 'Constant variables should not have leading underscore');
this.require(!node.name.startsWith('_'), node, 'Constant variables should not have leading underscore');
} else if (node.isStateVar) {
switch (node.visibility) {
case 'private':
this.require(node.name.startsWith('_'), node, 'Private state variables must have leading underscore');
break;
case 'internal':
this.require(node.name.startsWith('_'), node, 'Internal state variables must have leading underscore');
break;
case 'public':
this.require(!node.name.startsWith('_'), node, 'Public state variables should not have leading underscore');
break;
}
} else if (node.visibility === 'private' && !/^_/.test(node.name)) {
this.error(node, 'Non-constant private variables must have leading underscore');
}
}

FunctionDefinition(node) {
if (node.visibility === 'private' || (node.visibility === 'internal' && node.parent.kind !== 'library')) {
if (!/^_/.test(node.name)) {
this.error(node, 'Private and internal functions must have leading underscore');
}
}
if (node.visibility === 'internal' && node.parent.kind === 'library') {
if (/^_/.test(node.name)) {
this.error(node, 'Library internal functions should not have leading underscore');
}
switch (node.visibility) {
case 'external':
this.require(!node.name.startsWith('_'), node, 'External functions should not have leading underscore');
break;
case 'public':
this.require(!node.name.startsWith('_'), node, 'Public functions should not have leading underscore');
break;
case 'internal':
this.require(
node.name.startsWith('_') !== (node.parent.kind === 'library'),
node,
node.parent.kind === 'library'
? 'Library internal functions should not have leading underscore'
: 'Non-library internal functions must have leading underscore',
);
break;
case 'private':
this.require(node.name.startsWith('_'), node, 'Private functions must have leading underscore');
break;
}
}
},

// TODO: re-enable and fix
// class extends Base {
// static ruleId = 'no-external-virtual';
//
// FunctionDefinition(node) {
// if (node.visibility == 'external' && node.isVirtual) {
// this.error(node, 'Functions should not be external and virtual');
// }
// }
// },
class extends Base {
static ruleId = 'no-external-virtual';

FunctionDefinition(node) {
if (node.visibility == 'external') {
this.require(
node.isReceiveEther || node.isFallback || !node.isVirtual,
node,
'Functions should not be external and virtual',
);
}
}
},
];
2 changes: 1 addition & 1 deletion test/metatx/ERC2771Forwarder.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract ERC2771ForwarderMock is ERC2771Forwarder {
_hashTypedDataV4(
keccak256(
abi.encode(
_FORWARD_REQUEST_TYPEHASH,
FORWARD_REQUEST_TYPEHASH,
request.from,
request.to,
request.value,
Expand Down