Skip to content
Draft

v0.5.7 #1076

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions snapshots/Hub.Operations.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"draw": "105931",
"eliminateDeficit: full": "59781",
"eliminateDeficit: partial": "69429",
"mintFeeShares": "84007",
"mintFeeShares": "86130",
"payFee": "72302",
"refreshPremium": "71999",
"remove: full": "76993",
"remove: partial": "81640",
"reportDeficit": "115225",
"restore: full": "80471",
"restore: full - with transfer": "173377",
"restore: full - with transfer": "173521",
"restore: partial": "89137",
"restore: partial - with transfer": "147400",
"transferShares": "71192"
Expand Down
4 changes: 2 additions & 2 deletions snapshots/NativeTokenGateway.Operations.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"borrowNative": "229316",
"repayNative": "168024",
"borrowNative": "229604",
"repayNative": "168312",
"supplyAsCollateralNative": "160373",
"supplyNative": "136476",
"withdrawNative: full": "125620",
Expand Down
8 changes: 4 additions & 4 deletions snapshots/SignatureGateway.Operations.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"borrowWithSig": "215605",
"repayWithSig": "188872",
"setSelfAsUserPositionManagerWithSig": "75402",
"borrowWithSig": "215893",
"repayWithSig": "189160",
"setSelfAsUserPositionManagerWithSig": "74858",
"setUsingAsCollateralWithSig": "85053",
"supplyWithSig": "153205",
"updateUserDynamicConfigWithSig": "62769",
"updateUserRiskPremiumWithSig": "61579",
"withdrawWithSig": "131696"
"withdrawWithSig": "131713"
}
14 changes: 7 additions & 7 deletions snapshots/Spoke.Operations.ZeroRiskPremium.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"borrow: first": "191325",
"borrow: second action, same reserve": "171297",
"liquidationCall (receiveShares): full": "300103",
"liquidationCall (receiveShares): partial": "299821",
"liquidationCall: full": "310468",
"liquidationCall: partial": "310186",
"permitReserve + repay (multicall)": "166029",
"liquidationCall (receiveShares): full": "300391",
"liquidationCall (receiveShares): partial": "300109",
"liquidationCall: full": "310756",
"liquidationCall: partial": "310474",
"permitReserve + repay (multicall)": "166317",
"permitReserve + supply (multicall)": "146862",
"permitReserve + supply + enable collateral (multicall)": "160573",
"repay: full": "126094",
"repay: partial": "130983",
"repay: full": "126382",
"repay: partial": "131271",
"setUserPositionManagerWithSig: disable": "44846",
"setUserPositionManagerWithSig: enable": "68875",
"supply + enable collateral (multicall)": "140624",
Expand Down
30 changes: 15 additions & 15 deletions snapshots/Spoke.Operations.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"borrow: first": "261721",
"borrow: second action, same reserve": "204693",
"liquidationCall (receiveShares): full": "333666",
"liquidationCall (receiveShares): partial": "333384",
"liquidationCall: full": "344031",
"liquidationCall: partial": "343749",
"permitReserve + repay (multicall)": "163273",
"borrow: first": "262009",
"borrow: second action, same reserve": "204981",
"liquidationCall (receiveShares): full": "334242",
"liquidationCall (receiveShares): partial": "333960",
"liquidationCall: full": "344607",
"liquidationCall: partial": "344325",
"permitReserve + repay (multicall)": "163504",
"permitReserve + supply (multicall)": "146862",
"permitReserve + supply + enable collateral (multicall)": "160573",
"repay: full": "120256",
"repay: partial": "139545",
"repay: full": "120544",
"repay: partial": "139833",
"setUserPositionManagerWithSig: disable": "44846",
"setUserPositionManagerWithSig: enable": "68875",
"supply + enable collateral (multicall)": "140624",
Expand All @@ -18,16 +18,16 @@
"supply: second action, same reserve": "106579",
"updateUserDynamicConfig: 1 collateral": "73694",
"updateUserDynamicConfig: 2 collaterals": "88551",
"updateUserRiskPremium: 1 borrow": "151080",
"updateUserRiskPremium: 2 borrows": "204276",
"updateUserRiskPremium: 1 borrow": "151368",
"updateUserRiskPremium: 2 borrows": "204852",
"usingAsCollateral: 0 borrows, enable": "58915",
"usingAsCollateral: 1 borrow, disable": "161348",
"usingAsCollateral: 1 borrow, disable": "161636",
"usingAsCollateral: 1 borrow, enable": "41803",
"usingAsCollateral: 2 borrows, disable": "233712",
"usingAsCollateral: 2 borrows, disable": "234288",
"usingAsCollateral: 2 borrows, enable": "41815",
"withdraw: 0 borrows, full": "128910",
"withdraw: 0 borrows, partial": "133473",
"withdraw: 1 borrow, partial": "214810",
"withdraw: 2 borrows, partial": "259272",
"withdraw: 1 borrow, partial": "215098",
"withdraw: 2 borrows, partial": "259848",
"withdraw: non collateral": "106544"
}
1 change: 1 addition & 0 deletions src/hub/Hub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ contract Hub is IHub, AccessManaged {

/// @inheritdoc IHub
function mintFeeShares(uint256 assetId) external restricted returns (uint256) {
require(assetId < _assetCount, AssetNotListed());
Asset storage asset = _assets[assetId];
asset.accrue();
uint256 feeShares = _mintFeeShares(asset, assetId);
Expand Down
7 changes: 5 additions & 2 deletions src/libraries/math/MathUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.20;

import {SafeCast} from 'src/dependencies/openzeppelin/SafeCast.sol';

/// @title MathUtils library
/// @author Aave Labs
library MathUtils {
using SafeCast for uint256;

uint256 internal constant RAY = 1e27;
/// @dev Ignoring leap years
uint256 internal constant SECONDS_PER_YEAR = 365 days;
Expand Down Expand Up @@ -50,9 +54,8 @@ library MathUtils {
}

/// @notice Returns the difference of two unsigned integers as a signed integer.
/// @dev Does not ensure the `a` and `b` values are within the range of a signed integer.
function signedSub(uint256 a, uint256 b) internal pure returns (int256) {
return int256(a) - int256(b);
return a.toInt256() - b.toInt256();
}

/// @notice Returns the difference of two unsigned integers.
Expand Down
21 changes: 12 additions & 9 deletions src/position-manager/SignatureGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,21 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic
/// @inheritdoc ISignatureGateway
function setSelfAsUserPositionManagerWithSig(
address spoke,
EIP712Types.SetUserPositionManager calldata params,
address user,
bool approve,
uint256 nonce,
uint256 deadline,
bytes calldata signature
) external onlyRegisteredSpoke(spoke) {
try
ISpoke(spoke).setUserPositionManagerWithSig(
address(this),
params.user,
params.approve,
params.nonce,
params.deadline,
signature
)
ISpoke(spoke).setUserPositionManagerWithSig({
positionManager: address(this),
user: user,
approve: approve,
nonce: nonce,
deadline: deadline,
signature: signature
})
{} catch {}
}

Expand Down
12 changes: 9 additions & 3 deletions src/position-manager/interfaces/ISignatureGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,18 @@ interface ISignatureGateway is IMulticall, INoncesKeyed, IGatewayBase {
/// with a typed signature from `user`.
/// @dev The signature is consumed on the the specified registered `spoke`.
/// @dev The given data is passed to the `spoke` for the signature to be verified.
/// @param spoke The address of the spoke.
/// @param params The structured setSelfAsUserPositionManager parameters.
/// @param spoke The address of the registered spoke.
/// @param user The address of the user on whose behalf this gateway can act.
/// @param approve True to approve the gateway, false to revoke approval.
/// @param nonce The key-prefixed nonce for the signature.
/// @param deadline The deadline for the signature.
/// @param signature The signed bytes for the intent.
function setSelfAsUserPositionManagerWithSig(
address spoke,
EIP712Types.SetUserPositionManager calldata params,
address user,
bool approve,
uint256 nonce,
uint256 deadline,
bytes calldata signature
) external;

Expand Down
9 changes: 8 additions & 1 deletion tests/gas/Gateways.Operations.gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,14 @@ contract SignatureGateway_Gas_Tests is SignatureGatewayBaseTest {
vm.prank(alice);
spoke1.setUserPositionManager(address(gateway), false);

gateway.setSelfAsUserPositionManagerWithSig(address(spoke1), p, signature);
gateway.setSelfAsUserPositionManagerWithSig({
spoke: address(spoke1),
user: p.user,
approve: p.approve,
nonce: p.nonce,
deadline: p.deadline,
signature: signature
});
vm.snapshotGasLastCall(NAMESPACE, 'setSelfAsUserPositionManagerWithSig');
}
}
6 changes: 6 additions & 0 deletions tests/unit/Hub/Hub.MintFeeShares.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ contract HubMintFeeSharesTest is HubBase {
Utils.mintFeeShares(hub1, daiAssetId, ADMIN);
}

function test_mintFeeShares_revertsWith_AssetNotListed() public {
uint256 invalidAssetId = hub1.getAssetCount();
vm.expectRevert(IHub.AssetNotListed.selector);
Utils.mintFeeShares(hub1, invalidAssetId, ADMIN);
}

function test_mintFeeShares() public {
// Create debt to build up fees on the existing treasury spoke
_addAndDrawLiquidity({
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/MathUtils.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@ contract MathUtilsTest is Base {
assertTrue(result <= INT256_MAX);
}

function test_signedSub_revertsWith_SafeCastOverflowedUintToInt(uint256 a) public {
a = bound(a, uint256(INT256_MAX) + 1, UINT256_MAX);
vm.expectRevert(abi.encodeWithSelector(SafeCast.SafeCastOverflowedUintToInt.selector, a));
MathUtils.signedSub(a, 0);

vm.expectRevert(abi.encodeWithSelector(SafeCast.SafeCastOverflowedUintToInt.selector, a));
MathUtils.signedSub(0, a);
}

function test_uncheckedSub(uint256 a, uint256 b) public pure {
uint256 result = a >= b ? a - b : UINT256_MAX - b + a + 1;
assertEq(MathUtils.uncheckedSub(a, b), result);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;

import 'tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol';

contract SignatureGatewaySetSelfAsUserPositionManagerTest is SignatureGatewayBaseTest {
function test_setSelfAsUserPositionManagerWithSig_revertsWith_SpokeNotRegistered() public {
vm.expectRevert(IGatewayBase.SpokeNotRegistered.selector);
vm.prank(vm.randomAddress());
gateway.setSelfAsUserPositionManagerWithSig({
spoke: address(spoke2),
user: vm.randomAddress(),
approve: vm.randomBool(),
nonce: vm.randomUint(),
deadline: vm.randomUint(),
signature: vm.randomBytes(72)
});
}

function test_setSelfAsUserPositionManagerWithSig_forwards_correct_call() public {
address user = vm.randomAddress();
bool approve = vm.randomBool();
uint256 nonce = vm.randomUint();
uint256 deadline = vm.randomUint();
bytes memory signature = vm.randomBytes(72);

vm.expectCall(
address(spoke1),
abi.encodeCall(
ISpoke.setUserPositionManagerWithSig,
(address(gateway), user, approve, nonce, deadline, signature)
),
1
);
vm.prank(vm.randomAddress());
gateway.setSelfAsUserPositionManagerWithSig({
spoke: address(spoke1),
user: user,
approve: approve,
nonce: nonce,
deadline: deadline,
signature: signature
});
}

function test_setSelfAsUserPositionManagerWithSig_ignores_underlying_spoke_reverts() public {
vm.mockCallRevert(
address(spoke1),
ISpoke.setUserPositionManagerWithSig.selector,
vm.randomBytes(64)
);

vm.prank(vm.randomAddress());
gateway.setSelfAsUserPositionManagerWithSig({
spoke: address(spoke1),
user: vm.randomAddress(),
approve: vm.randomBool(),
nonce: vm.randomUint(),
deadline: vm.randomUint(),
signature: vm.randomBytes(72)
});

assertFalse(spoke1.isPositionManager(alice, address(gateway)));
}

function test_setSelfAsUserPositionManagerWithSig() public {
uint192 nonceKey = _randomNonceKey();
vm.prank(alice);
spoke1.useNonce(nonceKey);
EIP712Types.SetUserPositionManager memory p = EIP712Types.SetUserPositionManager({
positionManager: address(gateway),
user: alice,
approve: true,
nonce: spoke1.nonces(alice, nonceKey), // note: this typed sig is forwarded to spoke
deadline: _warpBeforeRandomDeadline()
});
bytes memory signature = _sign(alicePk, _getTypedDataHash(spoke1, p));

vm.prank(SPOKE_ADMIN);
spoke1.updatePositionManager(address(gateway), true);
vm.prank(alice);
spoke1.setUserPositionManager(address(gateway), false);

gateway.setSelfAsUserPositionManagerWithSig({
spoke: address(spoke1),
user: p.user,
approve: p.approve,
nonce: p.nonce,
deadline: p.deadline,
signature: signature
});

assertTrue(spoke1.isPositionManager(alice, address(gateway)));
}
}
9 changes: 8 additions & 1 deletion tests/unit/misc/SignatureGateway/SignatureGateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,14 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest {
emit ISpoke.SetUserPositionManager(alice, address(gateway), p.approve);

vm.prank(vm.randomAddress());
gateway.setSelfAsUserPositionManagerWithSig(address(spoke1), p, signature);
gateway.setSelfAsUserPositionManagerWithSig({
spoke: address(spoke1),
user: p.user,
approve: p.approve,
nonce: p.nonce,
deadline: p.deadline,
signature: signature
});

_assertNonceIncrement(ISignatureGateway(address(spoke1)), alice, p.nonce); // note: nonce consumed on spoke
_assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice);
Expand Down
Loading