Skip to content

Commit 4ca83cd

Browse files
DhairyaSethimiguelmtzinf
authored andcommitted
feat: Optimise Math util functions (#861)
1 parent 0b87c00 commit 4ca83cd

14 files changed

+237
-191
lines changed

snapshots/Hub.Operations.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
2-
"add": "117256",
3-
"draw": "124448",
4-
"eliminateDeficit: full": "61345",
5-
"eliminateDeficit: partial": "70921",
6-
"payFee": "73327",
7-
"refreshPremium": "103280",
8-
"remove: full": "77252",
9-
"remove: partial": "83769",
10-
"reportDeficit": "121597",
11-
"restore: full": "100313",
12-
"restore: partial": "113945",
13-
"transferShares": "79049"
2+
"add": "117155",
3+
"draw": "124145",
4+
"eliminateDeficit: full": "61244",
5+
"eliminateDeficit: partial": "70820",
6+
"payFee": "72817",
7+
"refreshPremium": "103179",
8+
"remove: full": "77151",
9+
"remove: partial": "83668",
10+
"reportDeficit": "121289",
11+
"restore: full": "100005",
12+
"restore: partial": "113435",
13+
"transferShares": "78539"
1414
}
Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
{
2-
"borrow: first": "226273",
3-
"borrow: second action, same reserve": "206794",
4-
"liquidationCall: full": "311508",
5-
"liquidationCall: partial": "331486",
6-
"permitReserve + repay (multicall)": "266172",
7-
"permitReserve + supply (multicall)": "144267",
8-
"permitReserve + supply + enable collateral (multicall)": "178228",
9-
"repay: full": "158136",
10-
"repay: partial": "210004",
2+
"borrow: first": "225460",
3+
"borrow: second action, same reserve": "205981",
4+
"liquidationCall: full": "310700",
5+
"liquidationCall: partial": "329643",
6+
"permitReserve + repay (multicall)": "264945",
7+
"permitReserve + supply (multicall)": "144166",
8+
"permitReserve + supply + enable collateral (multicall)": "178127",
9+
"repay: full": "158035",
10+
"repay: partial": "209398",
1111
"setUserPositionManagerWithSig: disable": "44882",
1212
"setUserPositionManagerWithSig: enable": "68905",
13-
"supply + enable collateral (multicall)": "156170",
14-
"supply: 0 borrows, collateral disabled": "119146",
15-
"supply: 0 borrows, collateral enabled": "122273",
16-
"supply: 1 borrow": "122265",
17-
"supply: second action, same reserve": "105173",
13+
"supply + enable collateral (multicall)": "156069",
14+
"supply: 0 borrows, collateral disabled": "119045",
15+
"supply: 0 borrows, collateral enabled": "122172",
16+
"supply: 1 borrow": "122164",
17+
"supply: second action, same reserve": "105072",
1818
"updateUserDynamicConfig: 1 collateral": "73802",
1919
"updateUserDynamicConfig: 2 collaterals": "88662",
20-
"updateUserRiskPremium: 1 borrow": "162210",
21-
"updateUserRiskPremium: 2 borrows": "235557",
20+
"updateUserRiskPremium: 1 borrow": "160872",
21+
"updateUserRiskPremium: 2 borrows": "231846",
2222
"usingAsCollateral: 0 borrows, enable": "58976",
23-
"usingAsCollateral: 1 borrow, disable": "172469",
23+
"usingAsCollateral: 1 borrow, disable": "171131",
2424
"usingAsCollateral: 1 borrow, enable": "32298",
25-
"usingAsCollateral: 2 borrows, disable": "259153",
25+
"usingAsCollateral: 2 borrows, disable": "256477",
2626
"usingAsCollateral: 2 borrows, enable": "41876",
27-
"withdraw: 0 borrows, full": "129936",
28-
"withdraw: 0 borrows, partial": "136271",
29-
"withdraw: 1 borrow, partial": "228364",
30-
"withdraw: 2 borrows, partial": "315037",
31-
"withdraw: non collateral": "127837"
27+
"withdraw: 0 borrows, full": "129835",
28+
"withdraw: 0 borrows, partial": "136170",
29+
"withdraw: 1 borrow, partial": "226925",
30+
"withdraw: 2 borrows, partial": "312260",
31+
"withdraw: non collateral": "126292"
3232
}

snapshots/Spoke.Operations.json

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
{
2-
"borrow: first": "251773",
3-
"borrow: second action, same reserve": "215194",
4-
"liquidationCall: full": "320124",
5-
"liquidationCall: partial": "340102",
6-
"permitReserve + repay (multicall)": "274788",
7-
"permitReserve + supply (multicall)": "144267",
8-
"permitReserve + supply + enable collateral (multicall)": "178228",
9-
"repay: full": "152352",
10-
"repay: partial": "218620",
2+
"borrow: first": "250960",
3+
"borrow: second action, same reserve": "214381",
4+
"liquidationCall: full": "319316",
5+
"liquidationCall: partial": "338259",
6+
"permitReserve + repay (multicall)": "273561",
7+
"permitReserve + supply (multicall)": "144166",
8+
"permitReserve + supply + enable collateral (multicall)": "178127",
9+
"repay: full": "152251",
10+
"repay: partial": "218014",
1111
"setUserPositionManagerWithSig: disable": "44882",
1212
"setUserPositionManagerWithSig: enable": "68905",
13-
"supply + enable collateral (multicall)": "156170",
14-
"supply: 0 borrows, collateral disabled": "119146",
15-
"supply: 0 borrows, collateral enabled": "122273",
16-
"supply: 1 borrow": "122265",
17-
"supply: second action, same reserve": "105173",
13+
"supply + enable collateral (multicall)": "156069",
14+
"supply: 0 borrows, collateral disabled": "119045",
15+
"supply: 0 borrows, collateral enabled": "122172",
16+
"supply: 1 borrow": "122164",
17+
"supply: second action, same reserve": "105072",
1818
"updateUserDynamicConfig: 1 collateral": "73802",
1919
"updateUserDynamicConfig: 2 collaterals": "88662",
20-
"updateUserRiskPremium: 1 borrow": "179010",
21-
"updateUserRiskPremium: 2 borrows": "269157",
20+
"updateUserRiskPremium: 1 borrow": "177672",
21+
"updateUserRiskPremium: 2 borrows": "265446",
2222
"usingAsCollateral: 0 borrows, enable": "58976",
23-
"usingAsCollateral: 1 borrow, disable": "189269",
23+
"usingAsCollateral: 1 borrow, disable": "187931",
2424
"usingAsCollateral: 1 borrow, enable": "32298",
25-
"usingAsCollateral: 2 borrows, disable": "292753",
25+
"usingAsCollateral: 2 borrows, disable": "290077",
2626
"usingAsCollateral: 2 borrows, enable": "41876",
27-
"withdraw: 0 borrows, full": "129936",
28-
"withdraw: 0 borrows, partial": "136271",
29-
"withdraw: 1 borrow, partial": "245164",
30-
"withdraw: 2 borrows, partial": "348637",
31-
"withdraw: non collateral": "127837"
27+
"withdraw: 0 borrows, full": "129835",
28+
"withdraw: 0 borrows, partial": "136170",
29+
"withdraw: 1 borrow, partial": "243725",
30+
"withdraw: 2 borrows, partial": "345860",
31+
"withdraw: non collateral": "126292"
3232
}

src/libraries/math/MathUtils.sol

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,29 @@
22
// Copyright (c) 2025 Aave Labs
33
pragma solidity ^0.8.20;
44

5-
import {WadRayMath} from 'src/libraries/math/WadRayMath.sol';
6-
75
/// @title MathUtils library
86
/// @author Aave Labs
97
library MathUtils {
10-
using WadRayMath for uint256;
11-
8+
uint256 internal constant RAY = 1e27;
129
/// @dev Ignoring leap years
1310
uint256 internal constant SECONDS_PER_YEAR = 365 days;
1411

15-
/// @notice Function to calculate the interest accumulated using a linear interest rate formula.
16-
/// @dev Calculates interest rate from provided `lastUpdateTimestamp` until present.
17-
/// @param rate The interest rate, in ray.
12+
/// @notice Calculates the interest accumulated using a linear interest rate formula.
13+
/// @dev Reverts if `lastUpdateTimestamp` is greater than `block.timestamp`.
14+
/// @param rate The interest rate in Ray units.
1815
/// @param lastUpdateTimestamp The timestamp to calculate interest rate from.
19-
/// @return The interest rate linearly accumulated during the timeDelta, in ray.
16+
/// @return result The interest rate linearly accumulated during the time delta in Ray units.
2017
function calculateLinearInterest(
21-
uint256 rate,
18+
uint96 rate,
2219
uint32 lastUpdateTimestamp
23-
) internal view returns (uint256) {
24-
uint256 result = rate * (block.timestamp - uint256(lastUpdateTimestamp));
25-
unchecked {
26-
result = result / SECONDS_PER_YEAR;
20+
) internal view returns (uint256 result) {
21+
assembly ('memory-safe') {
22+
if gt(lastUpdateTimestamp, timestamp()) {
23+
revert(0, 0)
24+
}
25+
result := sub(timestamp(), lastUpdateTimestamp)
26+
result := add(div(mul(rate, result), SECONDS_PER_YEAR), RAY)
2727
}
28-
29-
return WadRayMath.RAY + result;
3028
}
3129

3230
/// @notice Returns the smaller of two unsigned integers.
@@ -75,7 +73,9 @@ library MathUtils {
7573

7674
/// @notice Multiplies `a` and `b` in 256 bits and divides the result by `c`, rounding down.
7775
/// @dev Reverts if division by zero or overflow occurs on intermediate multiplication.
76+
/// @return d = floor(a * b / c).
7877
function mulDivDown(uint256 a, uint256 b, uint256 c) internal pure returns (uint256 d) {
78+
// to avoid overflow, a <= type(uint256).max / b
7979
assembly ('memory-safe') {
8080
if iszero(c) {
8181
revert(0, 0)
@@ -89,16 +89,19 @@ library MathUtils {
8989

9090
/// @notice Multiplies `a` and `b` in 256 bits and divides the result by `c`, rounding up.
9191
/// @dev Reverts if division by zero or overflow occurs on intermediate multiplication.
92+
/// @return d = ceil(a * b / c).
9293
function mulDivUp(uint256 a, uint256 b, uint256 c) internal pure returns (uint256 d) {
94+
// to avoid overflow, a <= type(uint256).max / b
9395
assembly ('memory-safe') {
9496
if iszero(c) {
9597
revert(0, 0)
9698
}
9799
if iszero(or(iszero(b), iszero(gt(a, div(not(0), b))))) {
98100
revert(0, 0)
99101
}
100-
let product := mul(a, b)
101-
d := add(div(product, c), gt(mod(product, c), 0))
102+
d := mul(a, b)
103+
// add 1 if (a * b) % c > 0 to round up the division of (a * b) by c
104+
d := add(div(d, c), gt(mod(d, c), 0))
102105
}
103106
}
104107
}

src/libraries/math/WadRayMath.sol

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ library WadRayMath {
131131
/// @return b = a * WAD in Wad units.
132132
function toWad(uint256 a) internal pure returns (uint256 b) {
133133
// to avoid overflow, b/WAD == a
134-
assembly {
134+
assembly ('memory-safe') {
135135
b := mul(a, WAD)
136136

137137
if iszero(eq(div(b, WAD), a)) {
@@ -143,22 +143,38 @@ library WadRayMath {
143143
/// @notice Removes Wad precision from a given value, rounding down.
144144
/// @return b = a / WAD in Wad units.
145145
function fromWadDown(uint256 a) internal pure returns (uint256 b) {
146-
assembly {
146+
assembly ('memory-safe') {
147147
b := div(a, WAD)
148148
}
149149
}
150150

151151
/// @notice Converts value from basis points to Wad, rounding down.
152152
/// @dev Reverts if intermediate multiplication overflows.
153-
/// @return c = floor(a * WAD / PERCENTAGE_FACTOR) in Wad units.
154-
function bpsToWad(uint256 a) internal pure returns (uint256) {
155-
return (a * WAD) / PERCENTAGE_FACTOR;
153+
/// @return b = floor(a * WAD / PERCENTAGE_FACTOR) in Wad units.
154+
function bpsToWad(uint256 a) internal pure returns (uint256 b) {
155+
// to avoid overflow, b/WAD == a
156+
assembly ('memory-safe') {
157+
b := mul(a, WAD)
158+
if iszero(eq(div(b, WAD), a)) {
159+
revert(0, 0)
160+
}
161+
162+
b := div(b, PERCENTAGE_FACTOR)
163+
}
156164
}
157165

158166
/// @notice Converts value from basis points to Ray, rounding down.
159167
/// @dev Reverts if intermediate multiplication overflows.
160-
/// @return c = a * RAY / PERCENTAGE_FACTOR in Ray units.
161-
function bpsToRay(uint256 a) internal pure returns (uint256) {
162-
return (a * RAY) / PERCENTAGE_FACTOR;
168+
/// @return b = a * RAY / PERCENTAGE_FACTOR in Ray units.
169+
function bpsToRay(uint256 a) internal pure returns (uint256 b) {
170+
// to avoid overflow, b/RAY == a
171+
assembly ('memory-safe') {
172+
b := mul(a, RAY)
173+
if iszero(eq(div(b, RAY), a)) {
174+
revert(0, 0)
175+
}
176+
177+
b := div(b, PERCENTAGE_FACTOR)
178+
}
163179
}
164180
}

tests/Base.t.sol

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,7 +1857,7 @@ abstract contract Base is Test {
18571857
/// @dev Calculate expected debt index based on input params
18581858
function _calculateExpectedDrawnIndex(
18591859
uint256 initialDrawnIndex,
1860-
uint256 borrowRate,
1860+
uint96 borrowRate,
18611861
uint32 startTime
18621862
) internal view returns (uint256) {
18631863
return initialDrawnIndex.rayMulUp(MathUtils.calculateLinearInterest(borrowRate, startTime));
@@ -1867,7 +1867,7 @@ abstract contract Base is Test {
18671867
function calculateExpectedDebt(
18681868
uint256 initialDrawnShares,
18691869
uint256 initialDrawnIndex,
1870-
uint256 borrowRate,
1870+
uint96 borrowRate,
18711871
uint32 startTime
18721872
) internal view returns (uint256 newDrawnIndex, uint256 newDrawnDebt) {
18731873
newDrawnIndex = _calculateExpectedDrawnIndex(initialDrawnIndex, borrowRate, startTime);
@@ -1877,7 +1877,7 @@ abstract contract Base is Test {
18771877
/// @dev Calculate expected drawn debt based on specified borrow rate
18781878
function _calculateExpectedDrawnDebt(
18791879
uint256 initialDebt,
1880-
uint256 borrowRate,
1880+
uint96 borrowRate,
18811881
uint32 startTime
18821882
) internal view returns (uint256) {
18831883
return MathUtils.calculateLinearInterest(borrowRate, startTime).rayMulUp(initialDebt);
@@ -2575,4 +2575,8 @@ abstract contract Base is Test {
25752575
function _unpackNonce(uint256 keyNonce) internal pure returns (uint192 key, uint64 nonce) {
25762576
return (uint192(keyNonce >> 64), uint64(keyNonce));
25772577
}
2578+
2579+
function _bpsToRay(uint256 bps) internal pure returns (uint256) {
2580+
return (bps * WadRayMath.RAY) / PercentageMath.PERCENTAGE_FACTOR;
2581+
}
25782582
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from z3 import *
2+
3+
s = Optimize()
4+
5+
UINT256_MAX = IntVal(2**256 - 1)
6+
RAY = IntVal(10**27)
7+
SECONDS_PER_YEAR = IntVal(365 * 24 * 60 * 60)
8+
9+
rate = IntVal(2**96 - 1)
10+
lastUpdateTimestamp = IntVal(2**32 - 1)
11+
currentTimestamp = Int("currentTimestamp")
12+
elapsed = currentTimestamp - lastUpdateTimestamp
13+
14+
s.add(rate * elapsed <= UINT256_MAX)
15+
s.add(RAY + ((rate * elapsed) / SECONDS_PER_YEAR) <= UINT256_MAX)
16+
17+
s.maximize(currentTimestamp)
18+
19+
assert s.check() == sat
20+
m = s.model()
21+
print("currentTimestamp max =", m[currentTimestamp])

0 commit comments

Comments
 (0)