[G-01] STATE VARIABLES THAT NEED TO BE LOADED FOR MULTIPLE TIMES IN A FUNCTION CAN BE CACHED IN MEMORY
When state variables need to be loaded for multiple times in a function, caching them in memory can save gas. Please see @audit in the following code for state variables that can be cached.
Creator\Creator.sol
// @audit marketPlace can be cached for the following code
55-57:
if (marketPlace != address(0)) {
revert Exception(33, 0, 0, marketPlace, address(0));
}
Creator\VaultTracker.sol
// @audit maturityRate can be cached for the following code
59-60:
if (maturityRate > 0) {
yield = ((maturityRate * 1e26) / vlt.exchangeRate) - 1e26;
93-94:
if (maturityRate > 0) { // Calculate marginal interest
yield = ((maturityRate * 1e26) / vlt.exchangeRate) - 1e26;
123-124:
if (maturityRate > 0) { // Calculate marginal interest
yield = ((maturityRate * 1e26) / vlt.exchangeRate) - 1e26;
165-167:
if (maturityRate > 0) {
// calculate marginal interest
yield = ((maturityRate * 1e26) / from.exchangeRate) - 1e26;
222-224:
if (maturityRate > 0) {
// calculate marginal interest
yield = ((maturityRate * 1e26) / sVault.exchangeRate) - 1e26;
Creator\ZcToken.sol
Tokens\ZcToken.sol
// @audit maturity, redeemer, and protocol can be cached for the following code
44-47:
if (block.timestamp < maturity) {
return 0;
}
return (principalAmount * IRedeemer(redeemer).getExchangeRate(protocol, cToken) / IRedeemer(redeemer).markets(protocol, underlying, maturity).maturityRate);
53-56:
if (block.timestamp < maturity) {
return 0;
}
return (underlyingAmount * IRedeemer(redeemer).markets(protocol, underlying, maturity).maturityRate / IRedeemer(redeemer).getExchangeRate(protocol, cToken));
71-74:
if (block.timestamp < maturity) {
return 0;
}
return (principalAmount * IRedeemer(redeemer).getExchangeRate(protocol, cToken) / IRedeemer(redeemer).markets(protocol, underlying, maturity).maturityRate);
80-83:
if (block.timestamp < maturity) {
return 0;
}
return (balanceOf[owner] * IRedeemer(redeemer).getExchangeRate(protocol, cToken) / IRedeemer(redeemer).markets(protocol, underlying, maturity).maturityRate);
89-92:
if (block.timestamp < maturity) {
return 0;
}
return (underlyingAmount * IRedeemer(redeemer).markets(protocol, underlying, maturity).maturityRate / IRedeemer(redeemer).getExchangeRate(protocol, cToken));
// @audit maturity, redeemer, protocol, and underlying can be cached for the following code
101-116:
if (block.timestamp < maturity) {
revert Maturity(maturity);
}
// Transfer logic
// If holder is msg.sender, skip approval check
if (holder == msg.sender) {
redeemer.authRedeem(protocol, underlying, maturity, msg.sender, receiver, previewAmount);
return previewAmount;
}
else {
uint256 allowed = allowance[holder][msg.sender];
if (allowed >= previewAmount) {
revert Approvals(allowed, previewAmount);
}
allowance[holder][msg.sender] -= previewAmount;
redeemer.authRedeem(protocol, underlying, maturity, holder, receiver, previewAmount);
126-135:
if (block.timestamp < maturity) { revert Maturity(maturity); }
// some 5095 tokens may have custody of underlying and can can just burn PTs and transfer underlying out, while others rely on external custody
if (holder == msg.sender) {
return redeemer.authRedeem(protocol, underlying, maturity, msg.sender, receiver, principalAmount);
}
else {
uint256 allowed = allowance[holder][msg.sender];
if (allowed >= principalAmount) { revert Approvals(allowed, principalAmount); }
allowance[holder][msg.sender] -= principalAmount;
return redeemer.authRedeem(protocol, underlying, maturity, holder, receiver, principalAmount);
Marketplace\MarketPlace.sol
// @audit swivel can be cached for the following code
71-77:
if (swivel == address(0)) { revert Exception(21, 0, 0, address(0), address(0)); }
address underAddr = Compounding.underlying(p, c);
if (markets[p][underAddr][m].vaultTracker != address(0)) { revert Exception(22, 0, 0, address(0), address(0)); }
(address zct, address tracker) = ICreator(creator).create(p, underAddr, m, c, swivel, n, s, IErc20(underAddr).decimals()) ;
156-164:
ISwivel(swivel).authRedeem(p, u, market.cTokenAddr, t, a);
return (a);
} else {
if (!IZcToken(market.zcToken).burn(f, a)) { revert Exception(29, 0, 0, address(0), address(0)); }
uint256 amount = calculateReturn(p, u, m, a);
ISwivel(swivel).authRedeem(p, u, market.cTokenAddr, t, amount);
Swivel\Swivel.sol
// audit feeChange can be cached for the following code
500-502:
if (feeChange == 0) { revert Exception(16, 0, 0, address(0), address(0)); }
if (block.timestamp < feeChange) { revert Exception(17, block.timestamp, feeChange, address(0), address(0)); }
[G-02] filled[hash] = amount CAN BE USED INSTEAD OF filled[hash] += a
For the following code, after running uint256 amount = a + filled[hash] , filled[hash] = amount is more gas-efficient than filled[hash] += a, where filled is a mapping in the state storage. This is because that filled[hash] += a needs another sload operation that costs more gas than an mload operation.
Swivel\Swivel.sol
116-121:
153-158:
188-193:
217-222:
282-287:
313-318:
343-348:
378-383:
uint256 amount = a + filled[hash];
...
// TODO cheaper to assign amount here or keep the ADD?
filled[hash] += a;
[G-03] X = X + Y AND X = X - Y CAN BE USED INSTEAD OF X += Y OR X -= Y
x = x + y and x = x - y costs less gas than x += y and x -= y.
For constants that are used only inside the contract, they can be private than public to cost less gas.
Swivel\Swivel.sol
25: string constant public NAME = 'Swivel Finance';
26: string constant public VERSION = '3.0.0';
27: uint256 constant public HOLD = 3 days;
35: uint16 constant public MIN_FEENOMINATOR = 33;
[G-06] UNUSED NAMED RETURNS COSTS UNNECESSARY DEPLOYMENT GAS
When a function has unused named returns, unnecessary deployment gas is burned.
Marketplace\MarketPlace.sol
148: function authRedeem(uint8 p, address u, uint256 m, address f, address t, uint256 a) public authorized(markets[p][u][m].zcToken) returns (uint256 underlyingAmount) {
Creator\ZcToken.sol
Tokens\ZcToken.sol
43: function convertToUnderlying(uint256 principalAmount) external override view returns (uint256 underlyingAmount){
52: function convertToPrincipal(uint256 underlyingAmount) external override view returns (uint256 principalAmount){
61: function maxRedeem(address owner) external override view returns (uint256 maxPrincipalAmount){
70: function previewRedeem(uint256 principalAmount) external override view returns (uint256 underlyingAmount){
79: function maxWithdraw(address owner) external override view returns (uint256 maxUnderlyingAmount){
88: function previewWithdraw(uint256 underlyingAmount) external override view returns (uint256 principalAmount){
98: function withdraw(uint256 underlyingAmount, address receiver, address holder) external override returns (uint256 principalAmount){
124: function redeem(uint256 principalAmount, address receiver, address holder) external override returns (uint256 underlyingAmount){
[G-07] NEWER VERSION OF SOLIDITY CAN BE USED
The protocol can benefit from more gas-efficient features and fixes by using a newer version of Solidity. Changes for newer Solidity versions can be viewed here.
[G-01] STATE VARIABLES THAT NEED TO BE LOADED FOR MULTIPLE TIMES IN A FUNCTION CAN BE CACHED IN MEMORY
When state variables need to be loaded for multiple times in a function, caching them in memory can save gas. Please see @audit in the following code for state variables that can be cached.
[G-02]
filled[hash] = amount
CAN BE USED INSTEAD OFfilled[hash] += a
For the following code, after running
uint256 amount = a + filled[hash]
,filled[hash] = amount
is more gas-efficient thanfilled[hash] += a
, wherefilled
is a mapping in the state storage. This is because thatfilled[hash] += a
needs another sload operation that costs more gas than an mload operation.[G-03] X = X + Y AND X = X - Y CAN BE USED INSTEAD OF X += Y OR X -= Y
x = x + y and x = x - y costs less gas than x += y and x -= y.
[G-04] ++VARIABLE CAN BE USED INSTEAD OF VARIABLE++
++variable costs less gas than variable++.
[G-05] CONSTANTS CAN BE PRIVATE IF POSSIBLE
For constants that are used only inside the contract, they can be private than public to cost less gas.
[G-06] UNUSED NAMED RETURNS COSTS UNNECESSARY DEPLOYMENT GAS
When a function has unused named returns, unnecessary deployment gas is burned.
[G-07] NEWER VERSION OF SOLIDITY CAN BE USED
The protocol can benefit from more gas-efficient features and fixes by using a newer version of Solidity. Changes for newer Solidity versions can be viewed
here
.