Platform: Code4rena
Start Date: 26/09/2022
Pot Size: $50,000 USDC
Total HM: 13
Participants: 113
Period: 5 days
Judge: 0xean
Total Solo HM: 6
Id: 166
League: ETH
Rank: 7/113
Findings: 4
Award: $1,799.53
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0xDecorativePineapple, Jeiwan, berndartmueller, brgltd, kaden, rbserver
In TransferHelper.sol the safeTransfer
function is as follows:
function safeTransfer( address token, address to, uint256 value ) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); }
This function is utilized in a few different places in the contract. According to the Solidity docs),
The low-level functions call, delegatecall and staticcall return true as their first return value if the account called is non-existent, as part of the design of the EVM. Account existence must be checked prior to calling if needed.
As a result, if the tokens have not yet been deployed or have been destroyed, safeTransfer
will return success even though no transfer was executed.
If the token has not yet been deployed, no liquidity can be added. However, if the token has been destroyed, the pool will act as if the assets were sent even though they were not.
The pool contains tokens X and Y. Token X has a bug, and the contract is destroyed. Bob is not aware of the issue and swaps 10_000 Y tokens for X tokens. Bob successfully transfers 10_000 Y tokens to the pool but does not receive any X tokens in return. As a result, Bob loses 10_000 Y tokens.
Manual Code Review
It is recommended to check the contract’s existence prior to the low-level call in TransferHelper.safeTransfer
. This will ensure that a swap reverts if the token to be bought no longer exists, preventing the pool from accepting the token to be sold without returning any tokens in exchange.
#0 - IliaAzhel
2022-10-04T13:07:38Z
duplicate of #267
#1 - 0xean
2022-10-04T15:28:46Z
dupe of #86
🌟 Selected for report: 0xDecorativePineapple
Also found by: rbserver
1464.8726 USDC - $1,464.87
https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L479 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L548 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L906
The Algebra protocol does not appear to support rebasing/deflationary/inflationary tokens whose balance changes during transfers or over time. The necessary checks include at least verifying the amount of tokens transferred to contracts before and after the actual transfer to infer any fees/interest.
https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L479 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L548 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L906
Manual Code Review
🌟 Selected for report: 0xSmartContract
Also found by: 0xDecorativePineapple, 0xNazgul, 0xmatt, Jeiwan, Trust, berndartmueller, brgltd, catchup, ch13fd357r0y3r, cryptonue, ladboy233, minhtrng, neko_nyaa, rbserver, rvierdiiev, s3cunda
35.4829 USDC - $35.48
A front-run on the initialize
function of the Algebra protocol allows an attacker to set an unfair price and to drain assets from the first deposits.
Bellow is the initialize
function:
function initialize(uint160 initialPrice) external override { require(globalState.price == 0, 'AI'); // getTickAtSqrtRatio checks validity of initialPrice inside int24 tick = TickMath.getTickAtSqrtRatio(initialPrice); uint32 timestamp = _blockTimestamp(); IDataStorageOperator(dataStorageOperator).initialize(timestamp, tick); globalState.price = initialPrice; globalState.unlocked = true; globalState.tick = tick; emit Initialize(initialPrice, tick); }
There are no access controls on the function, so anyone could call it on a deployed pool. Initializing a pool with an incorrect price allows an attacker to generate profits from the initial liquidity provider’s deposits.
Manual Code Review
It is recommended to consider:
#0 - 0xean
2022-10-02T22:22:20Z
dupe of #84
🌟 Selected for report: 0xNazgul
Also found by: 0x1f8b, 0x52, 0xDecorativePineapple, 0xSmartContract, 0xmatt, Aeros, Aymen0909, Bnke0x0, Chom, CodingNameKiki, Deivitto, DimitarDimitrov, IllIllI, JC, Jeiwan, Lambda, Matin, Migue, Mukund, Ocean_Sky, Olivierdem, RaymondFam, RockingMiles, Rolezn, Ruhum, Satyam_Sharma, Shinchan, Tomo, Trabajo_de_mates, V_B, Waze, __141345__, a12jmx, ajtra, asutorufos, aysha, brgltd, bulej93, carrotsmuggler, catchup, cccz, chrisdior4, cryptonue, cryptphi, d3e4, defsec, delfin454000, durianSausage, erictee, fatherOfBlocks, gogo, kaden, karanctf, ladboy233, lukris02, mahdikarimi, martin, mics, natzuu, oyc_109, p_crypt0, pedr02b2, rbserver, reassor, rotcivegaf, rvierdiiev, sikorico, slowmoses, sorrynotsorry, tnevler, trustindistrust
52.0364 USDC - $52.04
In the AlgebraFactory.sol smart contract of the Algebra Protocol, the ownership can be transferred to a new owner via the setOwner
function. The owner can then perform several privileged actions, such as setting the farmingAddress
and setting the vaultAddress
.The impact is that, if an incorrect address (for example and address for which the private key is not known) is used accidentally, then it blocks several functionalities of the protocol.
Also, due to the fact that zero-address check isn't implemented in the setOwner
function, if the current owner calls that function but mistakenly passes address(0)
as the _owner
the ownership will be lost forever.
You could see See similar High Risk severity finding from Trail-of-Bits Audit of Hermez and similar Medium Risk severity finding from Trail-of-Bits Audit of Uniswap V3.
You could find bellow the implementation of the setOwner
function.
function setOwner(address _owner) external override onlyOwner { require(owner != _owner); emit Owner(_owner); owner = _owner; }
Manual Code Review
It is recommended to implement a 2-step function in order to transferOwnership
to a new owner. The first function will approve a new address as the pendingOwner
. The second function, which is only callable by the pendingOwner
will claim the pending Owner and will transfer the full ownership to the pendingOwner
.
If it needs to be possible to set the owner to address(0)
, it is advised to implement a renounceOwnership
function
#0 - sameepsi
2022-10-04T06:45:45Z
duplicate of #131
#1 - 0xean
2022-10-04T15:38:02Z
downgrading to QA.