Platform: Code4rena
Start Date: 14/10/2021
Pot Size: $50,000 USDC
Total HM: 3
Participants: 14
Period: 7 days
Judge: 0xean
Total Solo HM: 3
Id: 37
League: ETH
Rank: 7/14
Findings: 2
Award: $708.62
🌟 Selected for report: 5
🚀 Solo Findings: 0
🌟 Selected for report: TomFrenchBlockchain
159.2357 USDC - $159.24
TomFrench
Increased gas costs on several TempusPool
functions
TempusPool
s have a finalize
function which checks whether block.timestamp >= maturityTime
and flips the maturity
storage variable as well as setting maturityInterestRate
to the current interest rate.
maturity
is used in several places to check whether the pool has expired however checking this variable is more expensive than checking block.timestamp >= maturityTime
(due to the need for a SLOAD whereas maturityTime
is immutable so no SLOAD is needed). I'd recommend making a function matured() public view
to keep the readability.
However maturityInterestRate
still needs to be set correctly. This could be done by reading the current interest rate when matured()
returns true but maturityInterestRate == 0
this could cause issues with some functions which are currently view functions however.
Half solution:
Replace matured
state variable with a matured()
view function which returns maturityInterestRate > 0
. This removes an SSTORE from finalize
and an SLOAD
from any function which uses maturityInterestRate
as you can just check if it's greater than zero to see if the pool has matured.
Full solution:
Replace matured
state variable with a matured()
view function which returns block.timestamp >= maturityTime
. This combined with setting maturityInterestRate
when you see that matured() == true
and maturityInterestRate == 0
would remove the need for the finalize
function entirely.
#0 - mijovic
2021-10-20T06:48:59Z
Good gas optimization saves a lot of access to storage. We will definitely go with the full solution, as it removes one more step and function (finalize).
#1 - mijovic
2021-10-21T08:33:31Z
This is fixed with (once merged) https://github.com/tempus-finance/tempus-protocol/pull/368
🌟 Selected for report: TomFrenchBlockchain
159.2357 USDC - $159.24
TomFrench
Reduced flexibility of AMM + additional gas costs on swaps
As the relative payouts of the principal and yield tokens are fixed at the point of finalisation, there's no need to freeze the AMM as it will just rapidly be arbed to the final prices of each token. No funds will be lost by LPs.
Making this change would reduce gas costs as swaps won't have to check maturity (load the TempusPool then perform SLOAD for maturity state variable).
Remove beforeMaturity
modifier from AMM.
#0 - mijovic
2021-10-20T06:19:14Z
Very good suggestion to save gas.
#1 - mijovic
2021-10-21T08:27:13Z
🌟 Selected for report: TomFrenchBlockchain
Also found by: pauliax
71.6561 USDC - $71.66
TomFrench
Increased gas costs
On L189, we use a uint8 as the for loop variable: https://github.com/code-423n4/2021-10-tempus/blob/63f7639aad08f2bba717830ed81e0649f7fc23ee/contracts/amm/TempusAMM.sol#L189
Due to how the EVM natively works on 256 numbers, using a 8 bit number here introduces additional costs as the EVM has to properly enforce the limits of this smaller type.
See the warning at this link: https://docs.soliditylang.org/en/v0.8.0/internals/layout_in_storage.html#layout-of-state-variables-in-storage
Change i to be a uint256 and replace any similar uints which only exist in memory and aren't required to use a smaller type.
#0 - mijovic
2021-10-21T10:00:26Z
🌟 Selected for report: TomFrenchBlockchain
159.2357 USDC - $159.24
TomFrench
As the TempusAmm
only ever registers with the Balancer Vault with the two token specialization the GeneralPool
interface will never be used as the Vault will call the MinimalSwapInfoPool
hooks instead.
See here in the Balancer Vault code:
Remove the inheritance from BaseGeneralPool
and remove the functions highlighted in the link below. This will help reduce bytecode from the AMM factory and reduce deployment costs.
#0 - mijovic
2021-10-20T06:33:50Z
Good catch. This can be considered as gas saving as this decreases codesize of TempusAMM
#1 - mijovic
2021-10-21T08:35:29Z
#2 - 0xean
2021-10-27T03:20:38Z
marking as gas optimization.
🌟 Selected for report: TomFrenchBlockchain
159.2357 USDC - $159.24
TomFrench
Higher gas costs on transfers of tokens from user to TempusPool
Following the flow of tokens from the user to their the TempusPool contract
:
TempusController.depositBacking
, TempusController
transfers user's tokens to itself and approves relevant TempusPool
TempusController
calls TempusPool.deposit
which in turn transfers tokens from the TempusController
and then invests them.This first transfer is then superfluous as the TempusPool
trusts the TempusController
(it's the only contract which may call deposit
). We're then incurring the costs of 1 transfer
and 1 approve
unnecessarily.
As TempusPool
trusts TempusController
, TempusController
can transfer the tokens directly to TempusPool
and just tell it how much has been deposited.
L412-L414 of TempusController.sol
would then be replaced with:
// Deposit to directly to targetPool uint transferredYBT = yieldBearingToken.untrustedTransferFrom(msg.sender, targetPool, yieldTokenAmount);
#0 - mijovic
2021-10-26T10:17:56Z
Being fixed by https://github.com/tempus-finance/tempus-protocol/pull/381