Asymmetry contest - MiksuJak's results

A protocol to help diversify and decentralize liquid staking derivatives.

General Information

Platform: Code4rena

Start Date: 24/03/2023

Pot Size: $49,200 USDC

Total HM: 20

Participants: 246

Period: 6 days

Judge: Picodes

Total Solo HM: 1

Id: 226

League: ETH

Asymmetry Finance

Findings Distribution

Researcher Performance

Rank: 212/246

Findings: 1

Award: $10.79

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Report by MiksuJak

Gas Optimizations

IssueInstances
GAS-1Store balance calls return values in memory instead of calling them multiple times in one function2
GAS-2Store derivative address in memory whenever it is read from storage more than once3
GAS-3Do not recalculate localTotalWeights when a weight is adjusted or added2

[GAS-1] Store balance calls return values in memory instead of calling them multiple times in one function

Store <address>.balance() in a memory variable to save gas on addresses balance calls.

Instance 1:

File: contracts/SafEth/SafEth.sol | stake | Lines: 71-75

for (uint i = 0; i < derivativeCount; i++)
	underlyingValue +=
		(derivatives[i].ethPerDerivative(derivatives[i].balance()) *
			derivatives[i].balance()) /
		10 ** 18;

Could be changed to:

for (uint i = 0; i < derivativeCount; i++) {
	uint256 derivativeBalance = derivatives[i].balance();
	underlyingValue +=
		(derivatives[i].ethPerDerivative(derivativeBalance) *
			derivativeBalance) /
		10 ** 18;
}

Saves 8145 gas per call for SafEth-Integration.test.ts:78 test.

Instance 2:

File: contracts/SafEth/SafEth.sol | rebalanceToWeights | Lines: 140-143

for (uint i = 0; i < derivativeCount; i++) {
	if (derivatives[i].balance() > 0)
		derivatives[i].withdraw(derivatives[i].balance());
}

Could be changed to:

for (uint i = 0; i < derivativeCount; i++) {
	uint256 derivativeBalance = derivatives[i].balance();
	if (derivativeBalance > 0)
		derivatives[i].withdraw(derivativeBalance);
}

Saves 8154 gas for SafEth-Integration.test.ts:94 test. Saves 4760 gas for SafEth-Integration.test.ts:123 test.

[GAS-2] Store derivative address in memory whenever it is read from storage more than once

There are for loops iterating over derivatives and performing multiple calls on each derivative in these loops. Store derivative[i] in memory whenever it is used more than once in a single iteration.

Instance 1:

File: contracts/SafEth/SafEth.sol | stake | Lines: 71-75

for (uint i = 0; i < derivativeCount; i++)
   underlyingValue +=
   	(derivatives[i].ethPerDerivative(derivatives[i].balance()) *
   		derivatives[i].balance()) /
   	10 ** 18;

Could be changed to:

for (uint i = 0; i < derivativeCount; i++) {
	IDerivative derivative = derivatives[i];
	underlyingValue +=
		(derivative.ethPerDerivative(derivative.balance()) *
			derivative.balance()) /
		10 ** 18;
}

Saves ~537 gas per call for SafEth-Integration.test.ts:78 test.

Instance 2:

File: contracts/SafEth/SafEth.sol | unstake | Lines: 115-118

for (uint256 i = 0; i < derivativeCount; i++) {
   IDerivative derivative = derivatives[i];
   // withdraw a percentage of each asset based on the amount of safETH
   uint256 derivativeAmount = (derivative.balance() *
   	_safEthAmount) / safEthTotalSupply;
   if (derivativeAmount == 0) continue; // if derivative empty ignore
   derivative.withdraw(derivativeAmount);
}

Could be changed to:

for (uint256 i = 0; i < derivativeCount; i++) {
	// withdraw a percentage of each asset based on the amount of safETH
	uint256 derivativeAmount = (derivatives[i].balance() *
		_safEthAmount) / safEthTotalSupply;
	if (derivativeAmount == 0) continue; // if derivative empty ignore
	derivatives[i].withdraw(derivativeAmount);
}

Saves ~501 gas per call for SafEth-Integration.test.ts:86 test.

Instance 3:

File: contracts/SafEth/SafEth.sol | rebalanceToWeights | Lines: 140-143

for (uint i = 0; i < derivativeCount; i++) {
   if (derivatives[i].balance() > 0)
   	derivatives[i].withdraw(derivatives[i].balance());
}

Could be changed to:

for (uint i = 0; i < derivativeCount; i++) {
	if (derivatives[i].balance() > 0)
		derivatives[i].withdraw(derivatives[i].balance());
}

Saves 546 gas for SafEth-Integration.test.ts:94 test. Saves 353 gas for SafEth-Integration.test.ts:123 test.

[GAS-3] Do not recalculate totalWeights when a weight is adjusted or added

When a weight is adjusted or a new one is added, localTotalWeight are calculated iterating over weights array. It is not needed since we know both the value of totalWeight and a weight to be adjusted/added. Moreover weights array does not ever change without totalWeight being updated, so we know that weights.sum() is always equal to totalWeight.

Instance 1:

File: contracts/SafEth/SafEth.sol | adjustWeight | Lines: 169-173

weights[_derivativeIndex] = _weight;
uint256 localTotalWeight = 0;
for (uint256 i = 0; i < derivativeCount; i++)
	localTotalWeight += weights[i];
totalWeight = localTotalWeight;

Could be changed to:

totalWeight = totalWeight + _weight - weights[_derivativeIndex];
weights[_derivativeIndex] = _weight;

Saves 7284 gas per call.

Instance 2:

File: contracts/SafEth/SafEth.sol | addDerivative | Lines: 169-173

weights[derivativeCount] = _weight;
derivativeCount++;

uint256 localTotalWeight = 0;
for (uint256 i = 0; i < derivativeCount; i++)
	localTotalWeight += weights[i];
totalWeight = localTotalWeight;

Could be changed to:

weights[derivativeCount] = _weight;
derivativeCount++;
totalWeight += _weight;

Saves 506 gas + 2455 gas * weights.length per call.

#0 - c4-sponsor

2023-04-07T21:42:41Z

toshiSat marked the issue as sponsor acknowledged

#1 - c4-judge

2023-04-23T14:51:38Z

Picodes marked the issue as grade-b

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