From d52a714961b6108a28fea4cfdbc7d266aaba0583 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:13:55 +0530 Subject: [PATCH 1/4] feat: spoke skimmed action --- snapshots/NativeTokenGateway.Operations.json | 6 +- snapshots/SignatureGateway.Operations.json | 8 +- .../Spoke.Operations.ZeroRiskPremium.json | 26 ++-- snapshots/Spoke.Operations.json | 26 ++-- src/position-manager/GatewayBase.sol | 8 ++ src/position-manager/NativeTokenGateway.sol | 12 +- src/spoke/Spoke.sol | 126 ++++++++++++------ src/spoke/TreasurySpoke.sol | 8 ++ src/spoke/interfaces/ISpokeBase.sol | 12 ++ 9 files changed, 150 insertions(+), 82 deletions(-) diff --git a/snapshots/NativeTokenGateway.Operations.json b/snapshots/NativeTokenGateway.Operations.json index 45aeca137..7396eb257 100644 --- a/snapshots/NativeTokenGateway.Operations.json +++ b/snapshots/NativeTokenGateway.Operations.json @@ -1,8 +1,8 @@ { "borrowNative": "229316", - "repayNative": "168024", - "supplyAsCollateralNative": "160373", - "supplyNative": "136476", + "repayNative": "161689", + "supplyAsCollateralNative": "154020", + "supplyNative": "124442", "withdrawNative: full": "125620", "withdrawNative: partial": "136825" } \ No newline at end of file diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index 96eb0ef3e..210ce4b09 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { "borrowWithSig": "215605", - "repayWithSig": "188872", + "repayWithSig": "188929", "setSelfAsUserPositionManagerWithSig": "75402", "setUsingAsCollateralWithSig": "85053", - "supplyWithSig": "153205", - "updateUserDynamicConfigWithSig": "62769", - "updateUserRiskPremiumWithSig": "61579", + "supplyWithSig": "153235", + "updateUserDynamicConfigWithSig": "62791", + "updateUserRiskPremiumWithSig": "61624", "withdrawWithSig": "131696" } \ No newline at end of file diff --git a/snapshots/Spoke.Operations.ZeroRiskPremium.json b/snapshots/Spoke.Operations.ZeroRiskPremium.json index ed2faa23d..8cb56baf6 100644 --- a/snapshots/Spoke.Operations.ZeroRiskPremium.json +++ b/snapshots/Spoke.Operations.ZeroRiskPremium.json @@ -5,21 +5,21 @@ "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", + "permitReserve + repay (multicall)": "166108", + "permitReserve + supply (multicall)": "146899", + "permitReserve + supply + enable collateral (multicall)": "160610", + "repay: full": "126173", + "repay: partial": "131062", "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", + "supply + enable collateral (multicall)": "140661", + "supply: 0 borrows, collateral disabled": "123716", + "supply: 0 borrows, collateral enabled": "106638", + "supply: second action, same reserve": "106616", + "updateUserDynamicConfig: 1 collateral": "73716", + "updateUserDynamicConfig: 2 collaterals": "88573", + "updateUserRiskPremium: 1 borrow": "94849", + "updateUserRiskPremium: 2 borrows": "104664", "usingAsCollateral: 0 borrows, enable": "58915", "usingAsCollateral: 1 borrow, disable": "105072", "usingAsCollateral: 1 borrow, enable": "41803", diff --git a/snapshots/Spoke.Operations.json b/snapshots/Spoke.Operations.json index deed9c95d..f17c298fd 100644 --- a/snapshots/Spoke.Operations.json +++ b/snapshots/Spoke.Operations.json @@ -5,21 +5,21 @@ "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", + "permitReserve + repay (multicall)": "163336", + "permitReserve + supply (multicall)": "146899", + "permitReserve + supply + enable collateral (multicall)": "160610", + "repay: full": "120335", + "repay: partial": "139624", "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", + "supply + enable collateral (multicall)": "140661", + "supply: 0 borrows, collateral disabled": "123716", + "supply: 0 borrows, collateral enabled": "106638", + "supply: second action, same reserve": "106616", + "updateUserDynamicConfig: 1 collateral": "73716", + "updateUserDynamicConfig: 2 collaterals": "88573", + "updateUserRiskPremium: 1 borrow": "151125", + "updateUserRiskPremium: 2 borrows": "204321", "usingAsCollateral: 0 borrows, enable": "58915", "usingAsCollateral: 1 borrow, disable": "161348", "usingAsCollateral: 1 borrow, enable": "41803", diff --git a/src/position-manager/GatewayBase.sol b/src/position-manager/GatewayBase.sol index 3c081667d..01d319f73 100644 --- a/src/position-manager/GatewayBase.sol +++ b/src/position-manager/GatewayBase.sol @@ -51,6 +51,14 @@ abstract contract GatewayBase is IGatewayBase, Rescuable, Ownable2Step { return ISpoke(spoke).getReserve(reserveId).underlying; } + function _getReserveInfo( + address spoke, + uint256 reserveId + ) internal view returns (address hub, address underlying) { + ISpoke.Reserve memory reserve = ISpoke(spoke).getReserve(reserveId); + return (address(reserve.hub), address(reserve.underlying)); + } + /// @dev The `owner()` is the allowed caller for Rescuable methods. function _rescueGuardian() internal view override returns (address) { return owner(); diff --git a/src/position-manager/NativeTokenGateway.sol b/src/position-manager/NativeTokenGateway.sol index 343c24855..4cd86c02e 100644 --- a/src/position-manager/NativeTokenGateway.sol +++ b/src/position-manager/NativeTokenGateway.sol @@ -112,7 +112,7 @@ contract NativeTokenGateway is INativeTokenGateway, GatewayBase, ReentrancyGuard uint256 amount ) external payable nonReentrant onlyRegisteredSpoke(spoke) returns (uint256, uint256) { require(msg.value == amount, NativeAmountMismatch()); - address underlying = _getReserveUnderlying(spoke, reserveId); + (address hub, address underlying) = _getReserveInfo(spoke, reserveId); _validateParams(underlying, amount); uint256 userTotalDebt = ISpoke(spoke).getUserTotalDebt(reserveId, msg.sender); @@ -124,8 +124,8 @@ contract NativeTokenGateway is INativeTokenGateway, GatewayBase, ReentrancyGuard } _nativeWrapper.deposit{value: repayAmount}(); - _nativeWrapper.forceApprove(spoke, repayAmount); - (uint256 repaidShares, uint256 repaidAmount) = ISpoke(spoke).repay( + _nativeWrapper.safeTransfer(hub, repayAmount); + (uint256 repaidShares, uint256 repaidAmount) = ISpoke(spoke).repaySkimmed( reserveId, repayAmount, msg.sender @@ -150,12 +150,12 @@ contract NativeTokenGateway is INativeTokenGateway, GatewayBase, ReentrancyGuard address user, uint256 amount ) internal returns (uint256, uint256) { - address underlying = _getReserveUnderlying(spoke, reserveId); + (address hub, address underlying) = _getReserveInfo(spoke, reserveId); _validateParams(underlying, amount); _nativeWrapper.deposit{value: amount}(); - _nativeWrapper.forceApprove(spoke, amount); - return ISpoke(spoke).supply(reserveId, amount, user); + _nativeWrapper.safeTransfer(hub, amount); + return ISpoke(spoke).supplySkimmed(reserveId, amount, user); } function _validateParams(address underlying, uint256 amount) internal view { diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index d71faaa03..ba1a673c5 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -232,17 +232,16 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea uint256 amount, address onBehalfOf ) external onlyPositionManager(onBehalfOf) returns (uint256, uint256) { - Reserve storage reserve = _getReserve(reserveId); - UserPosition storage userPosition = _userPositions[onBehalfOf][reserveId]; - _validateSupply(reserve.flags); - - IERC20(reserve.underlying).safeTransferFrom(msg.sender, address(reserve.hub), amount); - uint256 suppliedShares = reserve.hub.add(reserve.assetId, amount); - userPosition.suppliedShares += suppliedShares.toUint120(); - - emit Supply(reserveId, msg.sender, onBehalfOf, suppliedShares, amount); + return _supply(reserveId, amount, onBehalfOf, false); + } - return (suppliedShares, amount); + /// @inheritdoc ISpokeBase + function supplySkimmed( + uint256 reserveId, + uint256 amount, + address onBehalfOf + ) external onlyPositionManager(onBehalfOf) returns (uint256, uint256) { + return _supply(reserveId, amount, onBehalfOf, true); } /// @inheritdoc ISpokeBase @@ -307,40 +306,16 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea uint256 amount, address onBehalfOf ) external onlyPositionManager(onBehalfOf) returns (uint256, uint256) { - Reserve storage reserve = _getReserve(reserveId); - UserPosition storage userPosition = _userPositions[onBehalfOf][reserveId]; - _validateRepay(reserve.flags); - - uint256 drawnIndex = reserve.hub.getAssetDrawnIndex(reserve.assetId); - (uint256 drawnDebtRestored, uint256 premiumDebtRayRestored) = userPosition - .calculateRestoreAmount(drawnIndex, amount); - uint256 restoredShares = drawnDebtRestored.rayDivDown(drawnIndex); - - IHubBase.PremiumDelta memory premiumDelta = userPosition.getPremiumDelta({ - drawnSharesTaken: restoredShares, - drawnIndex: drawnIndex, - riskPremium: _positionStatus[onBehalfOf].riskPremium, - restoredPremiumRay: premiumDebtRayRestored - }); - - uint256 totalDebtRestored = drawnDebtRestored + premiumDebtRayRestored.fromRayUp(); - IERC20(reserve.underlying).safeTransferFrom( - msg.sender, - address(reserve.hub), - totalDebtRestored - ); - reserve.hub.restore(reserve.assetId, drawnDebtRestored, premiumDelta); - - userPosition.applyPremiumDelta(premiumDelta); - userPosition.drawnShares -= restoredShares.toUint120(); - if (userPosition.drawnShares == 0) { - PositionStatus storage positionStatus = _positionStatus[onBehalfOf]; - positionStatus.setBorrowing(reserveId, false); - } - - emit Repay(reserveId, msg.sender, onBehalfOf, restoredShares, totalDebtRestored, premiumDelta); + return _repay(reserveId, amount, onBehalfOf, false); + } - return (restoredShares, totalDebtRestored); + /// @inheritdoc ISpokeBase + function repaySkimmed( + uint256 reserveId, + uint256 amount, + address onBehalfOf + ) external onlyPositionManager(onBehalfOf) returns (uint256, uint256) { + return _repay(reserveId, amount, onBehalfOf, true); } /// @inheritdoc ISpokeBase @@ -890,6 +865,71 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea } } + function _supply( + uint256 reserveId, + uint256 amount, + address onBehalfOf, + bool skim + ) internal returns (uint256, uint256) { + Reserve storage reserve = _getReserve(reserveId); + UserPosition storage userPosition = _userPositions[onBehalfOf][reserveId]; + _validateSupply(reserve.flags); + + if (!skim) { + IERC20(reserve.underlying).safeTransferFrom(msg.sender, address(reserve.hub), amount); + } + uint256 suppliedShares = reserve.hub.add(reserve.assetId, amount); + userPosition.suppliedShares += suppliedShares.toUint120(); + + emit Supply(reserveId, msg.sender, onBehalfOf, suppliedShares, amount); + + return (suppliedShares, amount); + } + + function _repay( + uint256 reserveId, + uint256 amount, + address onBehalfOf, + bool skim + ) internal returns (uint256, uint256) { + Reserve storage reserve = _getReserve(reserveId); + UserPosition storage userPosition = _userPositions[onBehalfOf][reserveId]; + _validateRepay(reserve.flags); + + uint256 drawnIndex = reserve.hub.getAssetDrawnIndex(reserve.assetId); + (uint256 drawnDebtRestored, uint256 premiumDebtRayRestored) = userPosition + .calculateRestoreAmount(drawnIndex, amount); + uint256 restoredShares = drawnDebtRestored.rayDivDown(drawnIndex); + + IHubBase.PremiumDelta memory premiumDelta = userPosition.getPremiumDelta({ + drawnSharesTaken: restoredShares, + drawnIndex: drawnIndex, + riskPremium: _positionStatus[onBehalfOf].riskPremium, + restoredPremiumRay: premiumDebtRayRestored + }); + + uint256 totalDebtRestored = drawnDebtRestored + premiumDebtRayRestored.fromRayUp(); + if (!skim) { + IERC20(reserve.underlying).safeTransferFrom( + msg.sender, + address(reserve.hub), + totalDebtRestored + ); + } + reserve.hub.restore(reserve.assetId, drawnDebtRestored, premiumDelta); + + userPosition.applyPremiumDelta(premiumDelta); + userPosition.drawnShares -= restoredShares.toUint120(); + if (userPosition.drawnShares == 0) { + PositionStatus storage positionStatus = _positionStatus[onBehalfOf]; + positionStatus.setBorrowing(reserveId, false); + } + + emit Repay(reserveId, msg.sender, onBehalfOf, restoredShares, totalDebtRestored, premiumDelta); + + return (restoredShares, totalDebtRestored); + } + function _getReserve(uint256 reserveId) internal view returns (Reserve storage) { Reserve storage reserve = _reserves[reserveId]; require(address(reserve.hub) != address(0), ReserveNotListed()); diff --git a/src/spoke/TreasurySpoke.sol b/src/spoke/TreasurySpoke.sol index 4b29c1315..18f19932a 100644 --- a/src/spoke/TreasurySpoke.sol +++ b/src/spoke/TreasurySpoke.sol @@ -78,6 +78,14 @@ contract TreasurySpoke is ITreasurySpoke, Ownable2Step { revert UnsupportedAction(); } + function supplySkimmed(uint256, uint256, address) external pure returns (uint256, uint256) { + revert UnsupportedAction(); + } + + function repaySkimmed(uint256, uint256, address) external pure returns (uint256, uint256) { + revert UnsupportedAction(); + } + /// @inheritdoc ISpokeBase function repay(uint256, uint256, address) external pure returns (uint256, uint256) { revert UnsupportedAction(); diff --git a/src/spoke/interfaces/ISpokeBase.sol b/src/spoke/interfaces/ISpokeBase.sol index 370c6b141..f649a4e3d 100644 --- a/src/spoke/interfaces/ISpokeBase.sol +++ b/src/spoke/interfaces/ISpokeBase.sol @@ -107,6 +107,12 @@ interface ISpokeBase { address onBehalfOf ) external returns (uint256, uint256); + function supplySkimmed( + uint256 reserveId, + uint256 amount, + address onBehalfOf + ) external returns (uint256, uint256); + /// @notice Withdraws a specified amount of underlying asset from the given reserve. /// @dev It reverts if the reserve associated with the given reserve identifier is not listed. /// @dev Providing an amount greater than the maximum withdrawable value signals a full withdrawal. @@ -153,6 +159,12 @@ interface ISpokeBase { address onBehalfOf ) external returns (uint256, uint256); + function repaySkimmed( + uint256 reserveId, + uint256 amount, + address onBehalfOf + ) external returns (uint256, uint256); + /// @notice Liquidates a user position. /// @dev It reverts if the reserves associated with any of the given reserve identifiers are not listed. /// @dev The Spoke pulls underlying repaid debt assets from caller (Liquidator), hence it needs prior approval. From b289698bd9c0919ff426a9821370270378916acc Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:21:34 +0530 Subject: [PATCH 2/4] chore: bring up to codesize limit --- src/spoke/Spoke.sol | 6 +++++- src/spoke/instances/SpokeInstance.sol | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index ba1a673c5..812cfddb6 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -95,7 +95,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea /// @notice Modifier that checks if the caller is an approved positionManager for `onBehalfOf`. modifier onlyPositionManager(address onBehalfOf) { - require(_isPositionManager({user: onBehalfOf, manager: msg.sender}), Unauthorized()); + _onlyPositionManager({onBehalfOf: onBehalfOf, caller: msg.sender}); _; } @@ -1012,4 +1012,8 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea fnOut := fnIn } } + + function _onlyPositionManager(address onBehalfOf, address caller) private { + require(_isPositionManager({user: onBehalfOf, manager: caller}), Unauthorized()); + } } diff --git a/src/spoke/instances/SpokeInstance.sol b/src/spoke/instances/SpokeInstance.sol index 69a869dc9..60c65ab1d 100644 --- a/src/spoke/instances/SpokeInstance.sol +++ b/src/spoke/instances/SpokeInstance.sol @@ -9,7 +9,7 @@ import {Spoke} from 'src/spoke/Spoke.sol'; /// @author Aave Labs /// @notice Implementation contract for the Spoke. contract SpokeInstance is Spoke { - uint64 public constant SPOKE_REVISION = 1; + uint64 internal constant SPOKE_REVISION = 1; /// @dev Constructor. /// @dev During upgrade, must ensure that the new oracle is supporting existing assets on the spoke and the replaced oracle. From eb9178557ba4c55b00213e47649fad3801f75354 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:37:07 +0530 Subject: [PATCH 3/4] chore: snapshot --- snapshots/NativeTokenGateway.Operations.json | 12 ++-- snapshots/SignatureGateway.Operations.json | 14 ++--- snapshots/Spoke.Getters.json | 10 ++-- .../Spoke.Operations.ZeroRiskPremium.json | 58 +++++++++---------- snapshots/Spoke.Operations.json | 58 +++++++++---------- tests/unit/Spoke/Spoke.Upgradeable.t.sol | 1 - 6 files changed, 76 insertions(+), 77 deletions(-) diff --git a/snapshots/NativeTokenGateway.Operations.json b/snapshots/NativeTokenGateway.Operations.json index 7396eb257..74038eb46 100644 --- a/snapshots/NativeTokenGateway.Operations.json +++ b/snapshots/NativeTokenGateway.Operations.json @@ -1,8 +1,8 @@ { - "borrowNative": "229316", - "repayNative": "161689", - "supplyAsCollateralNative": "154020", - "supplyNative": "124442", - "withdrawNative: full": "125620", - "withdrawNative: partial": "136825" + "borrowNative": "229372", + "repayNative": "161657", + "supplyAsCollateralNative": "154110", + "supplyNative": "124476", + "withdrawNative: full": "125648", + "withdrawNative: partial": "136859" } \ No newline at end of file diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index 210ce4b09..85055ea25 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { - "borrowWithSig": "215605", - "repayWithSig": "188929", - "setSelfAsUserPositionManagerWithSig": "75402", - "setUsingAsCollateralWithSig": "85053", - "supplyWithSig": "153235", + "borrowWithSig": "215661", + "repayWithSig": "188919", + "setSelfAsUserPositionManagerWithSig": "75424", + "setUsingAsCollateralWithSig": "85109", + "supplyWithSig": "153262", "updateUserDynamicConfigWithSig": "62791", - "updateUserRiskPremiumWithSig": "61624", - "withdrawWithSig": "131696" + "updateUserRiskPremiumWithSig": "61601", + "withdrawWithSig": "131723" } \ No newline at end of file diff --git a/snapshots/Spoke.Getters.json b/snapshots/Spoke.Getters.json index 000034236..54b9c05ab 100644 --- a/snapshots/Spoke.Getters.json +++ b/snapshots/Spoke.Getters.json @@ -1,7 +1,7 @@ { - "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: 0, borrows: 0": "11959", + "getUserAccountData: supplies: 1, borrows: 0": "48622", + "getUserAccountData: supplies: 2, borrows: 0": "80400", + "getUserAccountData: supplies: 2, borrows: 1": "100188", + "getUserAccountData: supplies: 2, borrows: 2": "118618" } \ No newline at end of file diff --git a/snapshots/Spoke.Operations.ZeroRiskPremium.json b/snapshots/Spoke.Operations.ZeroRiskPremium.json index 8cb56baf6..c4516c5a6 100644 --- a/snapshots/Spoke.Operations.ZeroRiskPremium.json +++ b/snapshots/Spoke.Operations.ZeroRiskPremium.json @@ -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)": "166108", - "permitReserve + supply (multicall)": "146899", - "permitReserve + supply + enable collateral (multicall)": "160610", - "repay: full": "126173", - "repay: partial": "131062", - "setUserPositionManagerWithSig: disable": "44846", - "setUserPositionManagerWithSig: enable": "68875", - "supply + enable collateral (multicall)": "140661", - "supply: 0 borrows, collateral disabled": "123716", - "supply: 0 borrows, collateral enabled": "106638", - "supply: second action, same reserve": "106616", + "borrow: first": "191381", + "borrow: second action, same reserve": "171353", + "liquidationCall (receiveShares): full": "300125", + "liquidationCall (receiveShares): partial": "299843", + "liquidationCall: full": "310490", + "liquidationCall: partial": "310208", + "permitReserve + repay (multicall)": "166099", + "permitReserve + supply (multicall)": "146868", + "permitReserve + supply + enable collateral (multicall)": "160635", + "repay: full": "126229", + "repay: partial": "131118", + "setUserPositionManagerWithSig: disable": "44868", + "setUserPositionManagerWithSig: enable": "68897", + "supply + enable collateral (multicall)": "140686", + "supply: 0 borrows, collateral disabled": "123750", + "supply: 0 borrows, collateral enabled": "106672", + "supply: second action, same reserve": "106650", "updateUserDynamicConfig: 1 collateral": "73716", "updateUserDynamicConfig: 2 collaterals": "88573", - "updateUserRiskPremium: 1 borrow": "94849", - "updateUserRiskPremium: 2 borrows": "104664", - "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" + "updateUserRiskPremium: 1 borrow": "94826", + "updateUserRiskPremium: 2 borrows": "104641", + "usingAsCollateral: 0 borrows, enable": "58971", + "usingAsCollateral: 1 borrow, disable": "105128", + "usingAsCollateral: 1 borrow, enable": "41859", + "usingAsCollateral: 2 borrows, disable": "126111", + "usingAsCollateral: 2 borrows, enable": "41871", + "withdraw: 0 borrows, full": "128944", + "withdraw: 0 borrows, partial": "133507", + "withdraw: 1 borrow, partial": "161070", + "withdraw: 2 borrows, partial": "174248", + "withdraw: non collateral": "106578" } \ No newline at end of file diff --git a/snapshots/Spoke.Operations.json b/snapshots/Spoke.Operations.json index f17c298fd..18be0741b 100644 --- a/snapshots/Spoke.Operations.json +++ b/snapshots/Spoke.Operations.json @@ -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)": "163336", - "permitReserve + supply (multicall)": "146899", - "permitReserve + supply + enable collateral (multicall)": "160610", - "repay: full": "120335", - "repay: partial": "139624", - "setUserPositionManagerWithSig: disable": "44846", - "setUserPositionManagerWithSig: enable": "68875", - "supply + enable collateral (multicall)": "140661", - "supply: 0 borrows, collateral disabled": "123716", - "supply: 0 borrows, collateral enabled": "106638", - "supply: second action, same reserve": "106616", + "borrow: first": "261777", + "borrow: second action, same reserve": "204749", + "liquidationCall (receiveShares): full": "333688", + "liquidationCall (receiveShares): partial": "333406", + "liquidationCall: full": "344053", + "liquidationCall: partial": "343771", + "permitReserve + repay (multicall)": "163329", + "permitReserve + supply (multicall)": "146868", + "permitReserve + supply + enable collateral (multicall)": "160635", + "repay: full": "120391", + "repay: partial": "139680", + "setUserPositionManagerWithSig: disable": "44868", + "setUserPositionManagerWithSig: enable": "68897", + "supply + enable collateral (multicall)": "140686", + "supply: 0 borrows, collateral disabled": "123750", + "supply: 0 borrows, collateral enabled": "106672", + "supply: second action, same reserve": "106650", "updateUserDynamicConfig: 1 collateral": "73716", "updateUserDynamicConfig: 2 collaterals": "88573", - "updateUserRiskPremium: 1 borrow": "151125", - "updateUserRiskPremium: 2 borrows": "204321", - "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" + "updateUserRiskPremium: 1 borrow": "151102", + "updateUserRiskPremium: 2 borrows": "204298", + "usingAsCollateral: 0 borrows, enable": "58971", + "usingAsCollateral: 1 borrow, disable": "161404", + "usingAsCollateral: 1 borrow, enable": "41859", + "usingAsCollateral: 2 borrows, disable": "233768", + "usingAsCollateral: 2 borrows, enable": "41871", + "withdraw: 0 borrows, full": "128944", + "withdraw: 0 borrows, partial": "133507", + "withdraw: 1 borrow, partial": "214844", + "withdraw: 2 borrows, partial": "259306", + "withdraw: non collateral": "106578" } \ No newline at end of file diff --git a/tests/unit/Spoke/Spoke.Upgradeable.t.sol b/tests/unit/Spoke/Spoke.Upgradeable.t.sol index 65fc3c3c8..662351642 100644 --- a/tests/unit/Spoke/Spoke.Upgradeable.t.sol +++ b/tests/unit/Spoke/Spoke.Upgradeable.t.sol @@ -24,7 +24,6 @@ contract SpokeUpgradeableTest is SpokeBase { SpokeInstance spokeImpl = _deployMockSpokeInstance(revision); assertEq(address(spokeImpl), spokeImplAddress); - assertEq(spokeImpl.SPOKE_REVISION(), revision); assertEq(_getProxyInitializedVersion(spokeImplAddress), type(uint64).max); vm.expectRevert(Initializable.InvalidInitialization.selector); From 9deea6df02cacfa932c3a30e33c0a0f5dd4f5b39 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:22:52 +0530 Subject: [PATCH 4/4] chore: snapshot --- .../Spoke.Operations.ZeroRiskPremium.json | 5 +++ snapshots/Spoke.Operations.json | 5 +++ tests/gas/Spoke.Operations.gas.t.sol | 41 +++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/snapshots/Spoke.Operations.ZeroRiskPremium.json b/snapshots/Spoke.Operations.ZeroRiskPremium.json index c4516c5a6..8c82430c0 100644 --- a/snapshots/Spoke.Operations.ZeroRiskPremium.json +++ b/snapshots/Spoke.Operations.ZeroRiskPremium.json @@ -10,12 +10,17 @@ "permitReserve + supply + enable collateral (multicall)": "160635", "repay: full": "126229", "repay: partial": "131118", + "repaySkimmed: full": "110137", + "repaySkimmed: partial": "115026", "setUserPositionManagerWithSig: disable": "44868", "setUserPositionManagerWithSig: enable": "68897", "supply + enable collateral (multicall)": "140686", "supply: 0 borrows, collateral disabled": "123750", "supply: 0 borrows, collateral enabled": "106672", "supply: second action, same reserve": "106650", + "supplySkimmed: 0 borrows, collateral disabled": "107682", + "supplySkimmed: 0 borrows, collateral enabled": "90584", + "supplySkimmed: second action, same reserve": "90582", "updateUserDynamicConfig: 1 collateral": "73716", "updateUserDynamicConfig: 2 collaterals": "88573", "updateUserRiskPremium: 1 borrow": "94826", diff --git a/snapshots/Spoke.Operations.json b/snapshots/Spoke.Operations.json index 18be0741b..bc2ae547f 100644 --- a/snapshots/Spoke.Operations.json +++ b/snapshots/Spoke.Operations.json @@ -10,12 +10,17 @@ "permitReserve + supply + enable collateral (multicall)": "160635", "repay: full": "120391", "repay: partial": "139680", + "repaySkimmed: full": "104299", + "repaySkimmed: partial": "123588", "setUserPositionManagerWithSig: disable": "44868", "setUserPositionManagerWithSig: enable": "68897", "supply + enable collateral (multicall)": "140686", "supply: 0 borrows, collateral disabled": "123750", "supply: 0 borrows, collateral enabled": "106672", "supply: second action, same reserve": "106650", + "supplySkimmed: 0 borrows, collateral disabled": "107682", + "supplySkimmed: 0 borrows, collateral enabled": "90584", + "supplySkimmed: second action, same reserve": "90582", "updateUserDynamicConfig: 1 collateral": "73716", "updateUserDynamicConfig: 2 collaterals": "88573", "updateUserRiskPremium: 1 borrow": "151102", diff --git a/tests/gas/Spoke.Operations.gas.t.sol b/tests/gas/Spoke.Operations.gas.t.sol index aa6d2b8d9..00433c46f 100644 --- a/tests/gas/Spoke.Operations.gas.t.sol +++ b/tests/gas/Spoke.Operations.gas.t.sol @@ -34,6 +34,25 @@ contract SpokeOperations_Gas_Tests is SpokeBase { vm.stopPrank(); } + function test_supplySkimmed() public { + vm.startPrank(alice); + tokenList.usdx.transfer(address(_hub(spoke, reserveId.usdx)), 1000e6); + spoke.supplySkimmed(reserveId.usdx, 1000e6, alice); + vm.snapshotGasLastCall(NAMESPACE, 'supplySkimmed: 0 borrows, collateral disabled'); + + tokenList.usdx.transfer(address(_hub(spoke, reserveId.usdx)), 1000e6); + spoke.supplySkimmed(reserveId.usdx, 1000e6, alice); + vm.snapshotGasLastCall(NAMESPACE, 'supplySkimmed: second action, same reserve'); + + spoke.supply(reserveId.weth, 1000e18, alice); + + tokenList.weth.transfer(address(_hub(spoke, reserveId.weth)), 1e18); + spoke.setUsingAsCollateral(reserveId.weth, true, alice); + spoke.supplySkimmed(reserveId.weth, 1e18, alice); + vm.snapshotGasLastCall(NAMESPACE, 'supplySkimmed: 0 borrows, collateral enabled'); + vm.stopPrank(); + } + function test_usingAsCollateral() public { vm.prank(bob); spoke.supply(reserveId.dai, 1000e18, bob); @@ -135,6 +154,28 @@ contract SpokeOperations_Gas_Tests is SpokeBase { vm.stopPrank(); } + function test_repaySkimmed() public { + vm.prank(bob); + spoke.supply(reserveId.dai, 1000e18, bob); + + vm.startPrank(alice); + spoke.supply(reserveId.usdx, 1000e6, alice); + spoke.setUsingAsCollateral(reserveId.usdx, true, alice); + spoke.borrow(reserveId.dai, 500e18, alice); + + tokenList.dai.transfer(address(_hub(spoke, reserveId.dai)), 200e18); + spoke.repaySkimmed(reserveId.dai, 200e18, alice); + vm.snapshotGasLastCall(NAMESPACE, 'repaySkimmed: partial'); + + tokenList.dai.transfer( + address(_hub(spoke, reserveId.dai)), + spoke.getUserTotalDebt(reserveId.dai, alice) + ); + spoke.repaySkimmed(reserveId.dai, type(uint256).max, alice); + vm.snapshotGasLastCall(NAMESPACE, 'repaySkimmed: full'); + vm.stopPrank(); + } + function test_liquidation_partial() public { _liquidationSetup();