Skip to content
Open
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
12 changes: 6 additions & 6 deletions snapshots/NativeTokenGateway.Operations.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"borrowNative": "229316",
"repayNative": "168024",
"supplyAsCollateralNative": "160373",
"supplyNative": "136476",
"withdrawNative: full": "125620",
"withdrawNative: partial": "136825"
"borrowNative": "229792",
"repayNative": "168492",
"supplyAsCollateralNative": "161316",
"supplyNative": "136858",
"withdrawNative: full": "125996",
"withdrawNative: partial": "137294"
}
14 changes: 7 additions & 7 deletions snapshots/SignatureGateway.Operations.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"borrowWithSig": "215605",
"repayWithSig": "188872",
"borrowWithSig": "216081",
"repayWithSig": "189340",
"setSelfAsUserPositionManagerWithSig": "75402",
"setUsingAsCollateralWithSig": "85053",
"supplyWithSig": "153205",
"updateUserDynamicConfigWithSig": "62769",
"updateUserRiskPremiumWithSig": "61579",
"withdrawWithSig": "131696"
"setUsingAsCollateralWithSig": "85519",
"supplyWithSig": "153587",
"updateUserDynamicConfigWithSig": "63235",
"updateUserRiskPremiumWithSig": "62045",
"withdrawWithSig": "132071"
}
4 changes: 2 additions & 2 deletions snapshots/Spoke.Getters.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"getUserAccountData: supplies: 0, borrows: 0": "11937",
"getUserAccountData: supplies: 1, borrows: 0": "48600",
"getUserAccountData: supplies: 2, borrows: 0": "80378",
"getUserAccountData: supplies: 2, borrows: 1": "100166",
"getUserAccountData: supplies: 2, borrows: 2": "118596"
"getUserAccountData: supplies: 2, borrows: 1": "100165",
"getUserAccountData: supplies: 2, borrows: 2": "118594"
}
58 changes: 29 additions & 29 deletions snapshots/Spoke.Operations.ZeroRiskPremium.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
{
"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",
"permitReserve + supply (multicall)": "146862",
"permitReserve + supply + enable collateral (multicall)": "160573",
"repay: full": "126094",
"repay: partial": "130983",
"borrow: first": "191801",
"borrow: second action, same reserve": "171773",
"liquidationCall (receiveShares): full": "300567",
"liquidationCall (receiveShares): partial": "300285",
"liquidationCall: full": "310932",
"liquidationCall: partial": "310650",
"permitReserve + repay (multicall)": "166498",
"permitReserve + supply (multicall)": "147339",
"permitReserve + supply + enable collateral (multicall)": "161516",
"repay: full": "126563",
"repay: partial": "131452",
"setUserPositionManagerWithSig: disable": "44846",
"setUserPositionManagerWithSig: enable": "68875",
"supply + enable collateral (multicall)": "140624",
"supply: 0 borrows, collateral disabled": "123679",
"supply: 0 borrows, collateral enabled": "106601",
"supply: second action, same reserve": "106579",
"updateUserDynamicConfig: 1 collateral": "73694",
"updateUserDynamicConfig: 2 collaterals": "88551",
"updateUserRiskPremium: 1 borrow": "94804",
"updateUserRiskPremium: 2 borrows": "104619",
"usingAsCollateral: 0 borrows, enable": "58915",
"usingAsCollateral: 1 borrow, disable": "105072",
"usingAsCollateral: 1 borrow, enable": "41803",
"usingAsCollateral: 2 borrows, disable": "126055",
"usingAsCollateral: 2 borrows, enable": "41815",
"withdraw: 0 borrows, full": "128910",
"withdraw: 0 borrows, partial": "133473",
"withdraw: 1 borrow, partial": "161036",
"withdraw: 2 borrows, partial": "174214",
"withdraw: non collateral": "106544"
"supply + enable collateral (multicall)": "141567",
"supply: 0 borrows, collateral disabled": "124156",
"supply: 0 borrows, collateral enabled": "107078",
"supply: second action, same reserve": "107056",
"updateUserDynamicConfig: 1 collateral": "74160",
"updateUserDynamicConfig: 2 collaterals": "89017",
"updateUserRiskPremium: 1 borrow": "95269",
"updateUserRiskPremium: 2 borrows": "105083",
"usingAsCollateral: 0 borrows, enable": "59381",
"usingAsCollateral: 1 borrow, disable": "105537",
"usingAsCollateral: 1 borrow, enable": "42269",
"usingAsCollateral: 2 borrows, disable": "126519",
"usingAsCollateral: 2 borrows, enable": "42281",
"withdraw: 0 borrows, full": "129379",
"withdraw: 0 borrows, partial": "133942",
"withdraw: 1 borrow, partial": "161504",
"withdraw: 2 borrows, partial": "174681",
"withdraw: non collateral": "107013"
}
58 changes: 29 additions & 29 deletions snapshots/Spoke.Operations.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
{
"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",
"permitReserve + supply (multicall)": "146862",
"permitReserve + supply + enable collateral (multicall)": "160573",
"repay: full": "120256",
"repay: partial": "139545",
"borrow: first": "262197",
"borrow: second action, same reserve": "205169",
"liquidationCall (receiveShares): full": "334130",
"liquidationCall (receiveShares): partial": "333848",
"liquidationCall: full": "344495",
"liquidationCall: partial": "344213",
"permitReserve + repay (multicall)": "163648",
"permitReserve + supply (multicall)": "147339",
"permitReserve + supply + enable collateral (multicall)": "161516",
"repay: full": "120725",
"repay: partial": "140014",
"setUserPositionManagerWithSig: disable": "44846",
"setUserPositionManagerWithSig: enable": "68875",
"supply + enable collateral (multicall)": "140624",
"supply: 0 borrows, collateral disabled": "123679",
"supply: 0 borrows, collateral enabled": "106601",
"supply: second action, same reserve": "106579",
"updateUserDynamicConfig: 1 collateral": "73694",
"updateUserDynamicConfig: 2 collaterals": "88551",
"updateUserRiskPremium: 1 borrow": "151080",
"updateUserRiskPremium: 2 borrows": "204276",
"usingAsCollateral: 0 borrows, enable": "58915",
"usingAsCollateral: 1 borrow, disable": "161348",
"usingAsCollateral: 1 borrow, enable": "41803",
"usingAsCollateral: 2 borrows, disable": "233712",
"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: non collateral": "106544"
"supply + enable collateral (multicall)": "141567",
"supply: 0 borrows, collateral disabled": "124156",
"supply: 0 borrows, collateral enabled": "107078",
"supply: second action, same reserve": "107056",
"updateUserDynamicConfig: 1 collateral": "74160",
"updateUserDynamicConfig: 2 collaterals": "89017",
"updateUserRiskPremium: 1 borrow": "151545",
"updateUserRiskPremium: 2 borrows": "204740",
"usingAsCollateral: 0 borrows, enable": "59381",
"usingAsCollateral: 1 borrow, disable": "161813",
"usingAsCollateral: 1 borrow, enable": "42269",
"usingAsCollateral: 2 borrows, disable": "234176",
"usingAsCollateral: 2 borrows, enable": "42281",
"withdraw: 0 borrows, full": "129379",
"withdraw: 0 borrows, partial": "133942",
"withdraw: 1 borrow, partial": "215278",
"withdraw: 2 borrows, partial": "259739",
"withdraw: non collateral": "107013"
}
26 changes: 17 additions & 9 deletions src/spoke/Spoke.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {SafeCast} from 'src/dependencies/openzeppelin/SafeCast.sol';
import {SafeERC20, IERC20} from 'src/dependencies/openzeppelin/SafeERC20.sol';
import {IERC20Permit} from 'src/dependencies/openzeppelin/IERC20Permit.sol';
import {SignatureChecker} from 'src/dependencies/openzeppelin/SignatureChecker.sol';
import {ReentrancyGuardTransient} from 'src/dependencies/openzeppelin/ReentrancyGuardTransient.sol';
import {AccessManagedUpgradeable} from 'src/dependencies/openzeppelin-upgradeable/AccessManagedUpgradeable.sol';
import {EIP712} from 'src/dependencies/solady/EIP712.sol';
import {MathUtils} from 'src/libraries/math/MathUtils.sol';
Expand All @@ -26,7 +27,14 @@ import {ISpokeBase, ISpoke} from 'src/spoke/interfaces/ISpoke.sol';
/// @author Aave Labs
/// @notice Handles risk configuration & borrowing strategy for reserves and user positions.
/// @dev Each reserve can be associated with a separate Hub.
abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradeable, EIP712 {
abstract contract Spoke is
ISpoke,
Multicall,
NoncesKeyed,
AccessManagedUpgradeable,
ReentrancyGuardTransient,
EIP712
{
using SafeCast for *;
using SafeERC20 for IERC20;
using MathUtils for *;
Expand Down Expand Up @@ -231,7 +239,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea
uint256 reserveId,
uint256 amount,
address onBehalfOf
) external onlyPositionManager(onBehalfOf) returns (uint256, uint256) {
) external nonReentrant onlyPositionManager(onBehalfOf) returns (uint256, uint256) {
Reserve storage reserve = _getReserve(reserveId);
UserPosition storage userPosition = _userPositions[onBehalfOf][reserveId];
_validateSupply(reserve.flags);
Expand All @@ -250,7 +258,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea
uint256 reserveId,
uint256 amount,
address onBehalfOf
) external onlyPositionManager(onBehalfOf) returns (uint256, uint256) {
) external nonReentrant onlyPositionManager(onBehalfOf) returns (uint256, uint256) {
Reserve storage reserve = _getReserve(reserveId);
UserPosition storage userPosition = _userPositions[onBehalfOf][reserveId];
_validateWithdraw(reserve.flags);
Expand Down Expand Up @@ -280,7 +288,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea
uint256 reserveId,
uint256 amount,
address onBehalfOf
) external onlyPositionManager(onBehalfOf) returns (uint256, uint256) {
) external nonReentrant onlyPositionManager(onBehalfOf) returns (uint256, uint256) {
Reserve storage reserve = _getReserve(reserveId);
UserPosition storage userPosition = _userPositions[onBehalfOf][reserveId];
PositionStatus storage positionStatus = _positionStatus[onBehalfOf];
Expand All @@ -306,7 +314,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea
uint256 reserveId,
uint256 amount,
address onBehalfOf
) external onlyPositionManager(onBehalfOf) returns (uint256, uint256) {
) external nonReentrant onlyPositionManager(onBehalfOf) returns (uint256, uint256) {
Reserve storage reserve = _getReserve(reserveId);
UserPosition storage userPosition = _userPositions[onBehalfOf][reserveId];
_validateRepay(reserve.flags);
Expand Down Expand Up @@ -350,7 +358,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea
address user,
uint256 debtToCover,
bool receiveShares
) external {
) external nonReentrant {
Reserve storage collateralReserve = _getReserve(collateralReserveId);
Reserve storage debtReserve = _getReserve(debtReserveId);
DynamicReserveConfig storage collateralDynConfig = _dynamicConfig[collateralReserveId][
Expand Down Expand Up @@ -404,7 +412,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea
uint256 reserveId,
bool usingAsCollateral,
address onBehalfOf
) external onlyPositionManager(onBehalfOf) {
) external nonReentrant onlyPositionManager(onBehalfOf) {
_validateSetUsingAsCollateral(_getReserve(reserveId).flags, usingAsCollateral);
PositionStatus storage positionStatus = _positionStatus[onBehalfOf];

Expand All @@ -424,7 +432,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea
}

/// @inheritdoc ISpoke
function updateUserRiskPremium(address onBehalfOf) external {
function updateUserRiskPremium(address onBehalfOf) external nonReentrant {
if (!_isPositionManager({user: onBehalfOf, manager: msg.sender})) {
_checkCanCall(msg.sender, msg.data);
}
Expand All @@ -433,7 +441,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea
}

/// @inheritdoc ISpoke
function updateUserDynamicConfig(address onBehalfOf) external {
function updateUserDynamicConfig(address onBehalfOf) external nonReentrant {
if (!_isPositionManager({user: onBehalfOf, manager: msg.sender})) {
_checkCanCall(msg.sender, msg.data);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {console2 as console} from 'forge-std/console2.sol';
// dependencies
import {AggregatorV3Interface} from 'src/dependencies/chainlink/AggregatorV3Interface.sol';
import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from 'src/dependencies/openzeppelin/TransparentUpgradeableProxy.sol';
import {ReentrancyGuardTransient} from 'src/dependencies/openzeppelin/ReentrancyGuardTransient.sol';
import {IERC20Metadata} from 'src/dependencies/openzeppelin/IERC20Metadata.sol';
import {SafeCast} from 'src/dependencies/openzeppelin/SafeCast.sol';
import {IERC20Errors} from 'src/dependencies/openzeppelin/IERC20Errors.sol';
Expand Down Expand Up @@ -80,6 +81,7 @@ import {MockSpoke} from 'tests/mocks/MockSpoke.sol';
import {MockERC1271Wallet} from 'tests/mocks/MockERC1271Wallet.sol';
import {MockSpokeInstance} from 'tests/mocks/MockSpokeInstance.sol';
import {MockSkimSpoke} from 'tests/mocks/MockSkimSpoke.sol';
import {MockReentrantHub} from 'tests/mocks/MockReentrantHub.sol';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more contracts into base :'(


abstract contract Base is Test {
using stdStorage for StdStorage;
Expand Down
21 changes: 21 additions & 0 deletions tests/mocks/MockReentrantHub.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;

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

contract MockReentrantHub {
using Address for address;

address public immutable TARGET;
bytes4 public immutable TARGET_SELECTOR;

constructor(address target, bytes4 targetSelector) {
TARGET = target;
TARGET_SELECTOR = targetSelector;
}

fallback() external {
TARGET.functionCall(bytes.concat(TARGET_SELECTOR, new bytes(1000)));
}
}
Loading
Loading