Skip to content
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

c4: gas optimization fixes #742

Merged
merged 17 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2c4ac9d
fix(c4-117-g03): state variables should be cached in stack variables
tmigone Oct 24, 2022
76865a4
fix(c4-117-g05): require()/revert() strings longer than 32 bytes cost…
tmigone Oct 24, 2022
18afb3a
fix(c4-117-g06): keccak256() should only need to be called on a speci…
tmigone Oct 24, 2022
ee5d3cb
fix(c4-117-g11): using > 0 costs more gas than != 0 when used on a ui…
tmigone Oct 24, 2022
71314dd
fix(c4-117-g12): splitting require() statements that use && saves gas
tmigone Oct 24, 2022
d5aece4
fix(c4-117-g15): require() or revert() statements that check input ar…
tmigone Oct 24, 2022
2ad9f4e
fix(c4-6-g07): expressions for constant values such as a call to kecc…
tmigone Oct 24, 2022
e0440a8
fix(c4-6-g08): duplicated require()/revert() checks should be refacto…
tmigone Oct 24, 2022
3805ca9
fix(c4-6-13): use calldata instead of memory for function parameters
tmigone Oct 24, 2022
54157a7
fix(c4-86-g07): emitting or returning storage values
tmigone Oct 24, 2022
7d29d6d
fix(c4-172-g02): set allowance to 0 instead of decreasing allowance b…
tmigone Oct 24, 2022
534d224
fix(c4-287-g01): remove unused local variable
tmigone Oct 24, 2022
0b7ad55
fix(c4-272): remove unnecessary use of safe math
tmigone Oct 24, 2022
d091781
fix(c4-272): remove unnecessary usage of safemath
tmigone Oct 24, 2022
1f85e12
fix: further optimize governed contract
tmigone Oct 24, 2022
27902cc
fix: add visibility to managed immutable vars
tmigone Oct 24, 2022
e3a668e
fix: update e2e tests to new error messages
tmigone Oct 24, 2022
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/gateway/GraphTokenGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITok
modifier onlyGovernorOrGuardian() {
require(
msg.sender == controller.getGovernor() || msg.sender == pauseGuardian,
"Only Governor or Guardian can call"
"Only Governor or Guardian"
);
_;
}
Expand Down
5 changes: 3 additions & 2 deletions contracts/gateway/L1GraphTokenGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess
* @param _escrow Address of the BridgeEscrow
*/
function setEscrowAddress(address _escrow) external onlyGovernor {
require(_escrow != address(0) && Address.isContract(_escrow), "INVALID_ESCROW");
require(_escrow != address(0), "INVALID_ESCROW");
require(Address.isContract(_escrow), "MUST_BE_CONTRACT");
escrow = _escrow;
emit EscrowAddressSet(_escrow);
}
Expand Down Expand Up @@ -202,8 +203,8 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess
bytes calldata _data
) external payable override notPaused returns (bytes memory) {
IGraphToken token = graphToken();
require(_amount != 0, "INVALID_ZERO_AMOUNT");
require(_l1Token == address(token), "TOKEN_NOT_GRT");

Choose a reason for hiding this comment

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

Isn't it better to delay this check after the next line as well?

Copy link
Member

Choose a reason for hiding this comment

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

Good catch, noted to be added in a future PR

require(_amount > 0, "INVALID_ZERO_AMOUNT");
require(_to != address(0), "INVALID_DESTINATION");

// nested scopes to avoid stack too deep errors
Expand Down
7 changes: 4 additions & 3 deletions contracts/governance/Governed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,16 @@ contract Governed {
* This function must called by the pending governor.
*/
function acceptOwnership() external {
address _pendingGovernor = pendingGovernor;
tmigone marked this conversation as resolved.
Show resolved Hide resolved
require(
pendingGovernor != address(0) && msg.sender == pendingGovernor,
_pendingGovernor != address(0) && msg.sender == _pendingGovernor,
"Caller must be pending governor"
);

address oldGovernor = governor;
address oldPendingGovernor = pendingGovernor;
address oldPendingGovernor = _pendingGovernor;

governor = pendingGovernor;
governor = _pendingGovernor;
pendingGovernor = address(0);

emit NewOwnership(oldGovernor, governor);
Expand Down
22 changes: 15 additions & 7 deletions contracts/governance/Managed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ contract Managed {
mapping(bytes32 => address) private addressCache;
uint256[10] private __gap;

// Immutables
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it better to be immutable than const? as there are no parametrization happening

Copy link
Contributor Author

@tmigone tmigone Oct 24, 2022

Choose a reason for hiding this comment

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

I think in this case it's the same as we are using bytes32 to store the value (https://docs.soliditylang.org/en/v0.8.11/contracts.html#constant-and-immutable-state-variables).

I went with immutable because the reports suggested it, but should be the same as far as I understand.

bytes32 immutable CURATION = keccak256("Curation");
tmigone marked this conversation as resolved.
Show resolved Hide resolved
bytes32 immutable EPOCH_MANAGER = keccak256("EpochManager");
bytes32 immutable REWARDS_MANAGER = keccak256("RewardsManager");
bytes32 immutable STAKING = keccak256("Staking");
bytes32 immutable GRAPH_TOKEN = keccak256("GraphToken");
bytes32 immutable GRAPH_TOKEN_GATEWAY = keccak256("GraphTokenGateway");

// -- Events --

event ParameterUpdated(string param);
Expand All @@ -50,7 +58,7 @@ contract Managed {
}

function _onlyGovernor() internal view {
require(msg.sender == controller.getGovernor(), "Caller must be Controller governor");
require(msg.sender == controller.getGovernor(), "Only Controller governor");
}

function _onlyController() internal view {
Expand Down Expand Up @@ -111,47 +119,47 @@ contract Managed {
* @return Curation contract registered with Controller
*/
function curation() internal view returns (ICuration) {
return ICuration(_resolveContract(keccak256("Curation")));
return ICuration(_resolveContract(CURATION));
}

/**
* @dev Return EpochManager interface.
* @return Epoch manager contract registered with Controller
*/
function epochManager() internal view returns (IEpochManager) {
return IEpochManager(_resolveContract(keccak256("EpochManager")));
return IEpochManager(_resolveContract(EPOCH_MANAGER));
}

/**
* @dev Return RewardsManager interface.
* @return Rewards manager contract registered with Controller
*/
function rewardsManager() internal view returns (IRewardsManager) {
return IRewardsManager(_resolveContract(keccak256("RewardsManager")));
return IRewardsManager(_resolveContract(REWARDS_MANAGER));
}

/**
* @dev Return Staking interface.
* @return Staking contract registered with Controller
*/
function staking() internal view returns (IStaking) {
return IStaking(_resolveContract(keccak256("Staking")));
return IStaking(_resolveContract(STAKING));
}

/**
* @dev Return GraphToken interface.
* @return Graph token contract registered with Controller
*/
function graphToken() internal view returns (IGraphToken) {
return IGraphToken(_resolveContract(keccak256("GraphToken")));
return IGraphToken(_resolveContract(GRAPH_TOKEN));
}

/**
* @dev Return GraphTokenGateway (L1 or L2) interface.
* @return Graph token gateway contract registered with Controller
*/
function graphTokenGateway() internal view returns (ITokenGateway) {
return ITokenGateway(_resolveContract(keccak256("GraphTokenGateway")));
return ITokenGateway(_resolveContract(GRAPH_TOKEN_GATEWAY));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion contracts/l2/gateway/L2GraphTokenGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran
bytes calldata _data
) public payable override notPaused nonReentrant returns (bytes memory) {
require(_l1Token == l1GRT, "TOKEN_NOT_GRT");
require(_amount > 0, "INVALID_ZERO_AMOUNT");
require(_amount != 0, "INVALID_ZERO_AMOUNT");
require(msg.value == 0, "INVALID_NONZERO_VALUE");
require(_to != address(0), "INVALID_DESTINATION");

Expand Down
10 changes: 4 additions & 6 deletions contracts/upgrades/GraphProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ contract GraphProxy is GraphProxyStorage {
* NOTE: Only the admin can call this function.
*/
function setAdmin(address _newAdmin) external ifAdmin {
require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
require(_newAdmin != address(0), "Admin cant be the zero address");
_setAdmin(_newAdmin);
}

Expand Down Expand Up @@ -138,11 +138,9 @@ contract GraphProxy is GraphProxyStorage {
*/
function _acceptUpgrade() internal {
address _pendingImplementation = _pendingImplementation();
require(Address.isContract(_pendingImplementation), "Implementation must be a contract");
require(
_pendingImplementation != address(0) && msg.sender == _pendingImplementation,
"Caller must be the pending implementation"
);
require(Address.isContract(_pendingImplementation), "Impl must be a contract");
require(_pendingImplementation != address(0), "Impl cannot be zero address");
require(msg.sender == _pendingImplementation, "Only pending implementation");

_setImplementation(_pendingImplementation);
_setPendingImplementation(address(0));
Expand Down
2 changes: 1 addition & 1 deletion contracts/upgrades/GraphUpgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ contract GraphUpgradeable {
* @dev Check if the caller is the implementation.
*/
modifier onlyImpl() {
require(msg.sender == _implementation(), "Caller must be the implementation");
require(msg.sender == _implementation(), "Only implementation");
_;
}

Expand Down
12 changes: 6 additions & 6 deletions e2e/deployment/config/l1/l1GraphTokenGateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,38 +34,38 @@ describe('[L1] L1GraphTokenGateway configuration', function () {
unauthorized.address,
unauthorized.address,
)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('setL2TokenAddress should revert', async function () {
const tx = L1GraphTokenGateway.connect(unauthorized).setL2TokenAddress(unauthorized.address)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('setL2CounterpartAddress should revert', async function () {
const tx = L1GraphTokenGateway.connect(unauthorized).setL2CounterpartAddress(
unauthorized.address,
)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('setEscrowAddress should revert', async function () {
const tx = L1GraphTokenGateway.connect(unauthorized).setEscrowAddress(unauthorized.address)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('addToCallhookWhitelist should revert', async function () {
const tx = L1GraphTokenGateway.connect(unauthorized).addToCallhookWhitelist(
unauthorized.address,
)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('removeFromCallhookWhitelist should revert', async function () {
const tx = L1GraphTokenGateway.connect(unauthorized).removeFromCallhookWhitelist(
unauthorized.address,
)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('finalizeInboundTransfer should revert', async function () {
Expand Down
6 changes: 3 additions & 3 deletions e2e/deployment/config/l2/l2GraphTokenGateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,19 @@ describe('[L2] L2GraphTokenGateway configuration', function () {

it('setL2Router should revert', async function () {
const tx = L2GraphTokenGateway.connect(unauthorized).setL2Router(unauthorized.address)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('setL1TokenAddress should revert', async function () {
const tx = L2GraphTokenGateway.connect(unauthorized).setL1TokenAddress(unauthorized.address)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('setL1CounterpartAddress should revert', async function () {
const tx = L2GraphTokenGateway.connect(unauthorized).setL1CounterpartAddress(
unauthorized.address,
)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('finalizeInboundTransfer should revert', async function () {
Expand Down
8 changes: 4 additions & 4 deletions test/curation/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('Curation:Config', () => {

it('reject set `defaultReserveRatio` if not allowed', async function () {
const tx = curation.connect(me.signer).setDefaultReserveRatio(defaults.curation.reserveRatio)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
})

Expand All @@ -79,7 +79,7 @@ describe('Curation:Config', () => {
const tx = curation
.connect(me.signer)
.setMinimumCurationDeposit(defaults.curation.minimumCurationDeposit)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
})

Expand All @@ -99,7 +99,7 @@ describe('Curation:Config', () => {

it('reject set `curationTaxPercentage` if not allowed', async function () {
const tx = curation.connect(me.signer).setCurationTaxPercentage(0)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
})

Expand All @@ -124,7 +124,7 @@ describe('Curation:Config', () => {
it('reject set `curationTokenMaster` if not allowed', async function () {
const newCurationTokenMaster = curation.address
const tx = curation.connect(me.signer).setCurationTokenMaster(newCurationTokenMaster)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
})
})
8 changes: 4 additions & 4 deletions test/disputes/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('DisputeManager:Config', () => {

it('reject set `arbitrator` if not allowed', async function () {
const tx = disputeManager.connect(me.signer).setArbitrator(arbitrator.address)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})

it('reject set `arbitrator` to address zero', async function () {
Expand All @@ -74,7 +74,7 @@ describe('DisputeManager:Config', () => {
it('reject set `minimumDeposit` if not allowed', async function () {
const newValue = toBN('1')
const tx = disputeManager.connect(me.signer).setMinimumDeposit(newValue)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
})

Expand All @@ -97,7 +97,7 @@ describe('DisputeManager:Config', () => {

it('reject set `fishermanRewardPercentage` if not allowed', async function () {
const tx = disputeManager.connect(me.signer).setFishermanRewardPercentage(50)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
})

Expand Down Expand Up @@ -127,7 +127,7 @@ describe('DisputeManager:Config', () => {

it('reject set `slashingPercentage` if not allowed', async function () {
const tx = disputeManager.connect(me.signer).setSlashingPercentage(50, 50)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
})
})
Expand Down
4 changes: 2 additions & 2 deletions test/gateway/bridgeEscrow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('BridgeEscrow', () => {
describe('approveAll', function () {
it('cannot be called by someone other than the governor', async function () {
const tx = bridgeEscrow.connect(tokenReceiver.signer).approveAll(spender.address)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
it('allows a spender to transfer GRT held by the contract', async function () {
expect(await grt.allowance(bridgeEscrow.address, spender.address)).eq(0)
Expand All @@ -62,7 +62,7 @@ describe('BridgeEscrow', () => {
describe('revokeAll', function () {
it('cannot be called by someone other than the governor', async function () {
const tx = bridgeEscrow.connect(tokenReceiver.signer).revokeAll(spender.address)
await expect(tx).revertedWith('Caller must be Controller governor')
await expect(tx).revertedWith('Only Controller governor')
})
it("revokes a spender's permission to transfer GRT held by the contract", async function () {
await bridgeEscrow.connect(governor.signer).approveAll(spender.address)
Expand Down
Loading