veRWA - Jorgect's results

Incentivization Primitive for Real World Assets on Canto

General Information

Platform: Code4rena

Start Date: 07/08/2023

Pot Size: $36,500 USDC

Total HM: 11

Participants: 125

Period: 3 days

Judge: alcueca

Total Solo HM: 4

Id: 274

League: ETH

Canto

Findings Distribution

Researcher Performance

Rank: 49/125

Findings: 1

Award: $31.67

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Awards

31.6666 USDC - $31.67

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
duplicate-182

External Links

Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L356

Vulnerability details

Impact

Users can not undelegate his position, this is because the contract is requiring that the end of the new delegatee has to be greater than the old delegatee.

to understand this letΒ΄s analise the function delegate:

file:/src/VotingEscrow.sol function delegate(address _addr) external nonReentrant { LockedBalance memory locked_ = locked[msg.sender]; ... if (delegatee == msg.sender) { // Delegate fromLocked = locked_; toLocked = locked[_addr]; } else if (_addr == msg.sender) { // Undelegate fromLocked = locked[delegatee]; toLocked = locked_; } ... require(toLocked.amount > 0, "Delegatee has no lock"); require(toLocked.end > block.timestamp, "Delegatee lock expired"); require(toLocked.end >= fromLocked.end, "Only delegate to longer lock"); ... }

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L356

This function has the next require:

require(toLocked.end >= fromLocked.end, "Only delegate to longer lock");

When a user delegate his power his delegatee end time have to be greater than the user; in this case user = fromLocked and delegatee = toLocked, and this is fine but what about is a user want undelegate? he can not because now user = toLocked and delegatee = fromLocked and user have less end than the delegatee.

however there is a way to undelegate, the user has to call increaseAmount function passing 1 wei and increasing his end time, but user is no aware of this and is not how the contract should work.

Proof of Concept

The two function below were taked from the test of the contract the only modification that i did was add time betwen two createLock functions.

function testSuccessDelegate() public { // successful delegate testSuccessCreateLock(); vm.warp(block.timestamp + 100 days); // modification vm.prank(user2); ve.createLock{value: LOCK_AMT}(LOCK_AMT); vm.prank(user1); ve.delegate(user2); (,,, address delegatee) = ve.locked(user1); assertEq(delegatee, user2); (,, int128 delegated,) = ve.locked(user2); assertEq(delegated, 2000000000000000000); console.log(ve.numbercall()); } function testSuccessUnDelegate() public { // successful undelegate testSuccessDelegate(); vm.prank(user1); ve.delegate(user1); (,,, address delegatee) = ve.locked(user1); assertEq(delegatee, user1); }

testSuccessUnDelegate is gonna fail but we analize the transaction is reverting in :

require(toLocked.end >= fromLocked.end, "Only delegate to longer lock");

This test in the repository is passing because both are created in the same transaction.

Tools Used

manual, foundry

FIDO DAO has the same logic in the delegate function:

if (_addr == msg.sender) { _undelegate(); return; }

Consider apply his approach. other solution is think in remove the require stament

Assessed type

Other

#0 - c4-pre-sort

2023-08-12T11:02:12Z

141345 marked the issue as duplicate of #178

#1 - c4-judge

2023-08-24T07:14:08Z

alcueca marked the issue as duplicate of #82

#2 - c4-judge

2023-08-24T07:20:40Z

alcueca changed the severity to 2 (Med Risk)

#3 - c4-judge

2023-08-24T07:29:08Z

alcueca marked the issue as satisfactory

#4 - c4-judge

2023-08-29T06:37:35Z

alcueca changed the severity to 3 (High Risk)

Awards

31.6666 USDC - $31.67

Labels

bug
3 (High Risk)
partial-50
upgraded by judge
duplicate-182

External Links

Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L390

Vulnerability details

Impact

User can not undelegate or redelegate is a malicius delegatee front run the user transaction increasing his amount of time to end calling increaseAmount.

Proof of Concept

We can explore this analyzing both functions:

file:src/VotingEscrow.sol function delegate(address _addr) external nonReentrant { ... } else if (_addr == msg.sender) { // Undelegate fromLocked = locked[delegatee]; toLocked = locked_; } else { // Re-delegate fromLocked = locked[delegatee]; toLocked = locked[_addr]; // Update owner lock if not involved in delegation locked[msg.sender] = locked_; } ... require(toLocked.end >= fromLocked.end, "Only delegate to longer lock"); ... }

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L390C1-L409C6

This function require that old delegatee end be less than the new delegatee.

Now analyse the increaseAmount function:

file:src/VotingEscrow.sol function increaseAmount(uint256 _value) external payable nonReentrant { LockedBalance memory locked_ = locked[msg.sender]; ... LockedBalance memory newLocked = _copyLock(locked_); newLocked.amount += int128(int256(_value)); newLocked.end = _floorToWeek(block.timestamp + LOCKTIME); if (delegatee == msg.sender) { // Undelegated lock action = LockAction.INCREASE_AMOUNT_AND_DELEGATION; newLocked.delegated += int128(int256(_value)); locked[msg.sender] = newLocked; _checkpoint(msg.sender, locked_, newLocked); } else { // Delegated lock, update sender's lock first locked[msg.sender] = newLocked; ... } emit Deposit(msg.sender, _value, unlockTime, action, block.timestamp); }

https://github.com/code-423n4/2023-08-verwa/blob/a693b4db05b9e202816346a6f9cada94f28a2698/src/VotingEscrow.sol#L288C5-L324C1

This function is increasing the time of the end by 5 years, so if a delegatee see in the mempool that he is gonna be undelegate or re delegate, he can front run that transaction calling increaseAmount passing 1 wei and increasing his end for 5 years more breaking the require(toLocked.end >= fromLocked.end, "Only delegate to longer lock") in delegate function.

Tools Used

manual

One recomendation is follow what FIAT DAO is doing in his VotingEscrow contract, other approach that the project can take is eliminate the increasing of the time end when user call increasAmount, there is not incentive to the user to call that function if he is gonna lock for 5 years more

Assessed type

Other

#0 - c4-pre-sort

2023-08-12T16:51:21Z

141345 marked the issue as duplicate of #116

#1 - c4-pre-sort

2023-08-13T14:35:05Z

141345 marked the issue as duplicate of #82

#2 - c4-judge

2023-08-24T07:20:40Z

alcueca changed the severity to 2 (Med Risk)

#3 - c4-judge

2023-08-24T07:28:58Z

alcueca marked the issue as partial-50

#4 - c4-pre-sort

2023-08-24T08:21:45Z

141345 marked the issue as not a duplicate

#5 - c4-pre-sort

2023-08-24T08:24:09Z

141345 marked the issue as duplicate of #375

#6 - c4-judge

2023-08-24T21:11:25Z

alcueca marked the issue as partial-50

#7 - c4-judge

2023-08-29T06:37:06Z

alcueca marked the issue as duplicate of #182

#8 - c4-judge

2023-08-29T06:37:36Z

alcueca changed the severity to 3 (High Risk)

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter