Innocent Turquoise Barracuda
High
The DLOImplementation
contract's initialize
function fails to validate critical parameters during initialization, which could lead to a permanently broken contract state. Specifically, it doesn't validate:
- The aggregator contract address cannot be zero address
- The principle token address cannot be zero address
- The accepted collaterals array cannot be empty
- The maxLTVs array length must match accepted collaterals array length
This was confirmed through Echidna testing which demonstrated all these validations can fail.
There is no valid check of the paramters that are sent to the contract when initializing and this could make it contact useless for example no principle token which is literally what the user is trying to lend out.
No response
No response
No response
- HIGH SEVERITY
- If deployed with invalid parameters, the contract becomes permanently unusable since:
acceptLendingOffer
would fail due to zero address aggregator- Token transfers would fail due to zero address principle token
- No collateral could be accepted due to empty collateral array
- Inconsistent array lengths could cause out-of-bounds access or revert
- The contract cannot be reinitialized due to the initializer modifier, making these issues permanent
contract DLOImplementationEchidnaTest is DLOImplementation {
event Log (string value);
// Initialize Echidna with dummy addresses and parameters to set up the contract state
constructor() {
uint[] memory _ratios = new uint[](1);
address[] memory _acceptableCollateral = new address[](0);
address[] memory _oracleIDSCollateral = new address[](2);
bool[] memory _oracleActivated = new bool[](1);
uint[] memory _maxLTVs = new uint[](2);
_oracleIDSCollateral[0] = address(0);
// _acceptableCollateral[0] = address(0);
_oracleActivated[0] = true;
_maxLTVs[0] = 0;
_maxLTVs[1] = 0;
initialize(
address(0), // _aggregatorContract
true, // _perpetual
_oracleActivated,
true, // _lonelyLender
_maxLTVs,
5, // _apr
31536000, // _maxDuration (1 year)
86400, // _minDuration (1 day)
msg.sender, // _owner
address(0), // _principle
_acceptableCollateral, // _aedCollaterals
_oracleIDSCollateral, // _oraclCollateral
_ratios, // _ratio
address(0), // _oracleID_Principle
0 // _startedLendingAmount
);
}
function checkInvalidInitializer() public returns (bool) {
if (aggregatorContract == address(0)){
emit Log("Aggregator contract is address(0)");
}
if (lendInformation.principle == address(0)){
emit Log("principle is address(0)");
}
if (lendInformation.acceptedCollaterals.length == 0){
emit Log("acceptedCollaterals is zero in length");
}
if (lendInformation.maxLTVs.length != lendInformation.acceptedCollaterals.length){
emit Log("lendInformation.maxLTVs.length != lendInformation.acceptedCollaterals.length");
}
assert(aggregatorContract != address(0));
assert(lendInformation.principle != address(0));
assert(lendInformation.acceptedCollaterals.length > 0);
assert(lendInformation.maxLTVs.length == lendInformation.acceptedCollaterals.length);
return true;
}
The log on echidna
│ Workers: 0/4 │ Unique instructions: 3013 │ Chain ID: - │
│ Seed: 6835362162220902608│ Unique codehashes: 1 │ Fetched contracts: 0/1 │
│ Calls/s: 8359 │ Corpus size: 8 seqs │ Fetched slots: 0/0 │
│ Gas/s: 5567698 │ New coverage: 2s ago │ │
│ Total calls: 50158/50000 │ │ │
├──────────────────────────┴────────────────────────────────────────────── Tests (16) ────────┴────────────────────────────────────────────────────────────────┤
│ assertion in checkInvalidInitializer(): FAILED! with ErrorRevert ^│
│ █│
│ Call sequence: █│
│ 1. DLOImplementationEchidnaTest.checkInvalidInitializer() █│
│ █│
│ Traces: █│
│ emit Log(value=«Aggregator contract is address(0)») █│
│ (/Users/codertjay/GolandProjects/2024-11-debita-finance-v3-codertjay/Debita-V3-Contracts/contracts/echidna/EchidnaDebitaLendOffer-Implementation.sol:49) █│
│ emit Log(value=«principle is address(0)») █│
│ (/Users/codertjay/GolandProjects/2024-11-debita-finance-v3-codertjay/Debita-V3-Contracts/contracts/echidna/EchidnaDebitaLendOffer-Implementation.sol:52) █│
│ emit Log(value=«acceptedCollaterals is zero in length») █│
│ (/Users/codertjay/GolandProjects/2024-11-debita-finance-v3-codertjay/Debita-V3-Contracts/contracts/echidna/EchidnaDebitaLendOffer-Implementation.sol:57) █│
│ emit Log(value=«lendInformation.maxLTVs.length != lendInformation.acceptedCollaterals.length») █│
│ (/Users/codertjay/GolandProjects/2024-11-debita-finance-v3-codertjay/Debita-V3-Contracts/contracts/echidna/EchidnaDebitaLendOffer-Implementation.sol:61) │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │
│ AssertionFailed(..): passing │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │
│ assertion in acceptLendingOffer(uint256): passing │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │
│ assertion in aggregatorIsZero(): passing │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │
│ assertion in initialize(address,bool,bool[],bool,uint256[],uint256,uint256,uint256,address,address,address[],address[],uint256[],address,uint256): passing │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │
│ assertion in lendInformation(): passing v│
├────────────────────────────────────────────────────────────────────────── Log (14) ──────────────────────────────────────────────────────────────────────────┤
│ [2024-11-16 11:28:54.04] [Worker 1] Test limit reached. Stopping. ^│
│ [2024-11-16 11:28:54.00] [Worker 2] Test limit reached. Stopping. │
│ [2024-11-16 11:28:53.99] [Worker 3] Test limit reached. Stopping. │
│ [2024-11-16 11:28:53.97] [Worker 0] Test limit reached. Stopping. │
│ [2024-11-16 11:28:51.53] [Worker 2] New coverage: 3013 instr, 1 contracts, 8 seqs in corpus │
│ [2024-11-16 11:28:49.94] [Worker 3] New coverage: 3001 instr, 1 contracts, 7 seqs in corpus │
│ [2024-11-16 11:28:48.86] [Worker 2] New coverage: 2889 instr, 1 contracts, 6 seqs in corpus │
│ [2024-11-16 11:28:48.64] [Worker 3] New coverage: 2843 instr, 1 contracts, 5 seqs in corpus │
│ [2024-11-16 11:28:48.50] [Worker 2] New coverage: 2388 instr, 1 contracts, 4 seqs in corpus │
│ [2024-11-16 11:28:48.48] [Worker 0] New coverage: 2388 instr, 1 contracts, 3 seqs in corpus │
│ [2024-11-16 11:28:48.48] [Worker 3] New coverage: 2388 instr, 1 contracts, 2 seqs in corpus │
│ [2024-11-16 11:28:48.47] [Worker 1] New coverage: 2388 instr, 1 contracts, 1 seqs in corpus │
│ [2024-11-16 11:28:48.38] [Worker 2] Test checkInvalidInitializer() falsified! │
│ [2024-11-16 11:28:48.38] [Worker 0] Test checkInvalidInitializer() falsified! v│
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Campaign complete, C-c or esc to exit │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ```
### Mitigation
1. Add input validation in the initialize function:
```solidity
function initialize(
address _aggregatorContract,
bool _perpetual,
bool[] memory _oraclesActivated,
bool _lonelyLender,
uint[] memory _maxLTVs,
uint _apr,
uint _maxDuration,
uint _minDuration,
address _owner,
address _principle,
address[] memory _acceptedCollaterals,
address[] memory _oracleIDS_Collateral,
uint[] memory _ratio,
address _oracleID_Principle,
uint _startedLendingAmount
) public initializer {
// Add validation checks
require(_aggregatorContract != address(0), "Invalid aggregator address");
require(_principle != address(0), "Invalid principle token address");
require(_acceptedCollaterals.length > 0, "Must accept at least one collateral");
require(_maxLTVs.length == _acceptedCollaterals.length, "Array length mismatch");
require(_ratio.length == _acceptedCollaterals.length, "Ratio array length mismatch");
require(_oracleIDS_Collateral.length == _acceptedCollaterals.length, "Oracle array length mismatch");
require(_oraclesActivated.length == _acceptedCollaterals.length, "Oracle activation array length mismatch");
// Rest of the initialization code...
}
- Add logic to verify token contracts exist:
require(
IERC20(_principle).totalSupply() >= 0,
"Invalid principle token contract"
);
for (uint i = 0; i < _acceptedCollaterals.length; i++) {
require(
IERC20(_acceptedCollaterals[i]).totalSupply() >= 0,
"Invalid collateral token contract"
);
}