From faf6d2710a03c331ce7660674d568d2d22299ef1 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:32:26 +0530 Subject: [PATCH 1/9] feat: abstract intent consumption to be reused --- snapshots/SignatureGateway.Operations.json | 14 +++--- src/misc/IntentConsumer.sol | 31 +++++++++++++ src/position-manager/SignatureGateway.sol | 44 ++++--------------- .../interfaces/ISignatureGateway.sol | 3 -- 4 files changed, 47 insertions(+), 45 deletions(-) create mode 100644 src/misc/IntentConsumer.sol diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index 96eb0ef3e..cba3c1975 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { - "borrowWithSig": "215605", - "repayWithSig": "188872", + "borrowWithSig": "215693", + "repayWithSig": "188960", "setSelfAsUserPositionManagerWithSig": "75402", - "setUsingAsCollateralWithSig": "85053", - "supplyWithSig": "153205", - "updateUserDynamicConfigWithSig": "62769", - "updateUserRiskPremiumWithSig": "61579", - "withdrawWithSig": "131696" + "setUsingAsCollateralWithSig": "85107", + "supplyWithSig": "153248", + "updateUserDynamicConfigWithSig": "62662", + "updateUserRiskPremiumWithSig": "61472", + "withdrawWithSig": "131766" } \ No newline at end of file diff --git a/src/misc/IntentConsumer.sol b/src/misc/IntentConsumer.sol new file mode 100644 index 000000000..6c80429b6 --- /dev/null +++ b/src/misc/IntentConsumer.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +// Copyright (c) 2025 Aave Labs +pragma solidity 0.8.28; + +import {SignatureChecker} from 'src/dependencies/openzeppelin/SignatureChecker.sol'; +import {EIP712} from 'src/dependencies/solady/EIP712.sol'; +import {NoncesKeyed} from 'src/utils/NoncesKeyed.sol'; + +abstract contract IntentConsumer is NoncesKeyed, EIP712 { + /// @notice Thrown when signature deadline has passed or signer is not `onBehalfOf`. + error InvalidSignature(); + + /// @dev Verifies the signature for given signer & intent hash, and consumes the keyed-nonce. + /// @param signer The address of the user. + /// @param intentHash The hash of the intent struct. + /// @param nonce The keyed-nonce for the intent. + /// @param deadline The deadline timestamp for the intent. + /// @param signature The signature bytes. + function _verifyAndConsumeIntent( + address signer, + bytes32 intentHash, + uint256 nonce, + uint256 deadline, + bytes calldata signature + ) internal { + require(block.timestamp <= deadline, InvalidSignature()); + bytes32 digest = _hashTypedData(intentHash); + require(SignatureChecker.isValidSignatureNow(signer, digest, signature), InvalidSignature()); + _useCheckedNonce(signer, nonce); + } +} diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index de2194201..5278f881d 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -2,12 +2,10 @@ // Copyright (c) 2025 Aave Labs pragma solidity 0.8.28; -import {SignatureChecker} from 'src/dependencies/openzeppelin/SignatureChecker.sol'; +import {IntentConsumer} from 'src/misc/IntentConsumer.sol'; import {SafeERC20, IERC20} from 'src/dependencies/openzeppelin/SafeERC20.sol'; import {IERC20Permit} from 'src/dependencies/openzeppelin/IERC20Permit.sol'; -import {EIP712} from 'src/dependencies/solady/EIP712.sol'; import {MathUtils} from 'src/libraries/math/MathUtils.sol'; -import {NoncesKeyed} from 'src/utils/NoncesKeyed.sol'; import {Multicall} from 'src/utils/Multicall.sol'; import {EIP712Hash, EIP712Types} from 'src/position-manager/libraries/EIP712Hash.sol'; import {GatewayBase} from 'src/position-manager/GatewayBase.sol'; @@ -20,7 +18,7 @@ import {ISignatureGateway} from 'src/position-manager/interfaces/ISignatureGatew /// @dev Contract must be an active & approved user position manager to execute spoke actions on user's behalf. /// @dev Uses keyed-nonces where each key's namespace nonce is consumed sequentially. Intents bundled through /// multicall can be executed independently in order of signed nonce & deadline; does not guarantee batch atomicity. -contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multicall, EIP712 { +contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentConsumer { using SafeERC20 for IERC20; using EIP712Hash for *; @@ -33,13 +31,10 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic EIP712Types.Supply calldata params, bytes calldata signature ) external onlyRegisteredSpoke(params.spoke) returns (uint256, uint256) { - require(block.timestamp <= params.deadline, InvalidSignature()); address spoke = params.spoke; uint256 reserveId = params.reserveId; address user = params.onBehalfOf; - bytes32 digest = _hashTypedData(params.hash()); - require(SignatureChecker.isValidSignatureNow(user, digest, signature), InvalidSignature()); - _useCheckedNonce(user, params.nonce); + _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); underlying.safeTransferFrom(user, address(this), params.amount); @@ -57,9 +52,7 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic address spoke = params.spoke; uint256 reserveId = params.reserveId; address user = params.onBehalfOf; - bytes32 digest = _hashTypedData(params.hash()); - require(SignatureChecker.isValidSignatureNow(user, digest, signature), InvalidSignature()); - _useCheckedNonce(user, params.nonce); + _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); (uint256 withdrawnShares, uint256 withdrawnAmount) = ISpoke(spoke).withdraw( @@ -81,9 +74,7 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic address spoke = params.spoke; uint256 reserveId = params.reserveId; address user = params.onBehalfOf; - bytes32 digest = _hashTypedData(params.hash()); - require(SignatureChecker.isValidSignatureNow(user, digest, signature), InvalidSignature()); - _useCheckedNonce(user, params.nonce); + _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); (uint256 borrowedShares, uint256 borrowedAmount) = ISpoke(spoke).borrow( @@ -105,9 +96,7 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic address spoke = params.spoke; uint256 reserveId = params.reserveId; address user = params.onBehalfOf; - bytes32 digest = _hashTypedData(params.hash()); - require(SignatureChecker.isValidSignatureNow(user, digest, signature), InvalidSignature()); - _useCheckedNonce(user, params.nonce); + _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); uint256 repayAmount = MathUtils.min( @@ -126,11 +115,8 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic EIP712Types.SetUsingAsCollateral calldata params, bytes calldata signature ) external onlyRegisteredSpoke(params.spoke) { - require(block.timestamp <= params.deadline, InvalidSignature()); address user = params.onBehalfOf; - bytes32 digest = _hashTypedData(params.hash()); - require(SignatureChecker.isValidSignatureNow(user, digest, signature), InvalidSignature()); - _useCheckedNonce(user, params.nonce); + _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); ISpoke(params.spoke).setUsingAsCollateral(params.reserveId, params.useAsCollateral, user); } @@ -140,13 +126,7 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic EIP712Types.UpdateUserRiskPremium calldata params, bytes calldata signature ) external onlyRegisteredSpoke(params.spoke) { - require(block.timestamp <= params.deadline, InvalidSignature()); - bytes32 digest = _hashTypedData(params.hash()); - require( - SignatureChecker.isValidSignatureNow(params.user, digest, signature), - InvalidSignature() - ); - _useCheckedNonce(params.user, params.nonce); + _verifyAndConsumeIntent(params.user, params.hash(), params.nonce, params.deadline, signature); ISpoke(params.spoke).updateUserRiskPremium(params.user); } @@ -156,13 +136,7 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic EIP712Types.UpdateUserDynamicConfig calldata params, bytes calldata signature ) external onlyRegisteredSpoke(params.spoke) { - require(block.timestamp <= params.deadline, InvalidSignature()); - bytes32 digest = _hashTypedData(params.hash()); - require( - SignatureChecker.isValidSignatureNow(params.user, digest, signature), - InvalidSignature() - ); - _useCheckedNonce(params.user, params.nonce); + _verifyAndConsumeIntent(params.user, params.hash(), params.nonce, params.deadline, signature); ISpoke(params.spoke).updateUserDynamicConfig(params.user); } diff --git a/src/position-manager/interfaces/ISignatureGateway.sol b/src/position-manager/interfaces/ISignatureGateway.sol index d7ae8d6cd..6074f3fc8 100644 --- a/src/position-manager/interfaces/ISignatureGateway.sol +++ b/src/position-manager/interfaces/ISignatureGateway.sol @@ -11,9 +11,6 @@ import {IGatewayBase} from 'src/position-manager/interfaces/IGatewayBase.sol'; /// @author Aave Labs /// @notice Minimal interface for protocol actions involving signed intents. interface ISignatureGateway is IMulticall, INoncesKeyed, IGatewayBase { - /// @notice Thrown when signature deadline has passed or signer is not `onBehalfOf`. - error InvalidSignature(); - /// @notice Facilitates `supply` action on the specified registered `spoke` with a typed signature from `onBehalfOf`. /// @dev Supplied assets are pulled from `onBehalfOf`, prior approval to this gateway is required. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. From 345d7309cfd0c63f91867c960f4f022b9407ab98 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:32:21 +0530 Subject: [PATCH 2/9] fix: migrate spoke --- snapshots/NativeTokenGateway.Operations.json | 4 +- snapshots/SignatureGateway.Operations.json | 10 +-- snapshots/Spoke.Getters.json | 10 +-- .../Spoke.Operations.ZeroRiskPremium.json | 28 +++---- snapshots/Spoke.Operations.json | 28 +++---- src/position-manager/SignatureGateway.sol | 14 +--- src/position-manager/libraries/EIP712Hash.sol | 20 +++++ src/spoke/Spoke.sol | 41 +++------- src/spoke/interfaces/ISpoke.sol | 16 +--- tests/Base.t.sol | 1 + tests/gas/Spoke.Operations.gas.t.sol | 18 +--- .../Spoke.SetUserPositionManagerWithSig.t.sol | 82 ++++--------------- ...tureGateway.Reverts.InvalidSignature.t.sol | 28 +++---- 13 files changed, 109 insertions(+), 191 deletions(-) diff --git a/snapshots/NativeTokenGateway.Operations.json b/snapshots/NativeTokenGateway.Operations.json index 45aeca137..51f656579 100644 --- a/snapshots/NativeTokenGateway.Operations.json +++ b/snapshots/NativeTokenGateway.Operations.json @@ -1,6 +1,6 @@ { - "borrowNative": "229316", - "repayNative": "168024", + "borrowNative": "229294", + "repayNative": "168089", "supplyAsCollateralNative": "160373", "supplyNative": "136476", "withdrawNative: full": "125620", diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index cba3c1975..3ed8cda2f 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { - "borrowWithSig": "215693", - "repayWithSig": "188960", - "setSelfAsUserPositionManagerWithSig": "75402", + "borrowWithSig": "215593", + "repayWithSig": "188947", + "setSelfAsUserPositionManagerWithSig": "76464", "setUsingAsCollateralWithSig": "85107", - "supplyWithSig": "153248", + "supplyWithSig": "153186", "updateUserDynamicConfigWithSig": "62662", "updateUserRiskPremiumWithSig": "61472", - "withdrawWithSig": "131766" + "withdrawWithSig": "131704" } \ No newline at end of file diff --git a/snapshots/Spoke.Getters.json b/snapshots/Spoke.Getters.json index 000034236..aead49eba 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": "11915", + "getUserAccountData: supplies: 1, borrows: 0": "48578", + "getUserAccountData: supplies: 2, borrows: 0": "80356", + "getUserAccountData: supplies: 2, borrows: 1": "100144", + "getUserAccountData: supplies: 2, borrows: 2": "118574" } \ No newline at end of file diff --git a/snapshots/Spoke.Operations.ZeroRiskPremium.json b/snapshots/Spoke.Operations.ZeroRiskPremium.json index ed2faa23d..167c3e687 100644 --- a/snapshots/Spoke.Operations.ZeroRiskPremium.json +++ b/snapshots/Spoke.Operations.ZeroRiskPremium.json @@ -1,18 +1,18 @@ { - "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", - "setUserPositionManagerWithSig: disable": "44846", - "setUserPositionManagerWithSig: enable": "68875", - "supply + enable collateral (multicall)": "140624", + "borrow: first": "191303", + "borrow: second action, same reserve": "171275", + "liquidationCall (receiveShares): full": "300081", + "liquidationCall (receiveShares): partial": "299799", + "liquidationCall: full": "310446", + "liquidationCall: partial": "310164", + "permitReserve + repay (multicall)": "166072", + "permitReserve + supply (multicall)": "146840", + "permitReserve + supply + enable collateral (multicall)": "160551", + "repay: full": "126159", + "repay: partial": "131048", + "setUserPositionManagerWithSig: disable": "45813", + "setUserPositionManagerWithSig: enable": "69842", + "supply + enable collateral (multicall)": "140602", "supply: 0 borrows, collateral disabled": "123679", "supply: 0 borrows, collateral enabled": "106601", "supply: second action, same reserve": "106579", diff --git a/snapshots/Spoke.Operations.json b/snapshots/Spoke.Operations.json index deed9c95d..266a0a08c 100644 --- a/snapshots/Spoke.Operations.json +++ b/snapshots/Spoke.Operations.json @@ -1,18 +1,18 @@ { - "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", - "setUserPositionManagerWithSig: disable": "44846", - "setUserPositionManagerWithSig: enable": "68875", - "supply + enable collateral (multicall)": "140624", + "borrow: first": "261699", + "borrow: second action, same reserve": "204671", + "liquidationCall (receiveShares): full": "333644", + "liquidationCall (receiveShares): partial": "333362", + "liquidationCall: full": "344009", + "liquidationCall: partial": "343727", + "permitReserve + repay (multicall)": "163308", + "permitReserve + supply (multicall)": "146840", + "permitReserve + supply + enable collateral (multicall)": "160551", + "repay: full": "120321", + "repay: partial": "139610", + "setUserPositionManagerWithSig: disable": "45813", + "setUserPositionManagerWithSig: enable": "69842", + "supply + enable collateral (multicall)": "140602", "supply: 0 borrows, collateral disabled": "123679", "supply: 0 borrows, collateral enabled": "106601", "supply: second action, same reserve": "106579", diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index 5278f881d..5b37c6e1b 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -144,19 +144,11 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo /// @inheritdoc ISignatureGateway function setSelfAsUserPositionManagerWithSig( address spoke, - EIP712Types.SetUserPositionManager calldata params, + EIP712Types.SetUserPositionManager memory params, bytes calldata signature ) external onlyRegisteredSpoke(spoke) { - try - ISpoke(spoke).setUserPositionManagerWithSig( - address(this), - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ) - {} catch {} + params.positionManager = address(this); // todo will be removed with explicit args + try ISpoke(spoke).setUserPositionManagerWithSig(params, signature) {} catch {} } /// @inheritdoc ISignatureGateway diff --git a/src/position-manager/libraries/EIP712Hash.sol b/src/position-manager/libraries/EIP712Hash.sol index 060bd4575..2da917140 100644 --- a/src/position-manager/libraries/EIP712Hash.sol +++ b/src/position-manager/libraries/EIP712Hash.sol @@ -8,6 +8,10 @@ import {EIP712Types} from 'src/libraries/types/EIP712Types.sol'; /// @author Aave Labs /// @notice Helper methods to hash EIP712 typed data structs. library EIP712Hash { + bytes32 public constant SET_USER_POSITION_MANAGER_TYPEHASH = + // keccak256('SetUserPositionManager(address positionManager,address user,bool approve,uint256 nonce,uint256 deadline)') + 0x758d23a3c07218b7ea0b4f7f63903c4e9d5cbde72d3bcfe3e9896639025a0214; + bytes32 public constant SUPPLY_TYPEHASH = // keccak256('Supply(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)') 0xe85497eb293c001e8483fe105efadd1d50aa0dadfc0570b27058031dfceab2e6; @@ -138,4 +142,20 @@ library EIP712Hash { ) ); } + + function hash( + EIP712Types.SetUserPositionManager calldata params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + SET_USER_POSITION_MANAGER_TYPEHASH, + params.positionManager, + params.user, + params.approve, + params.nonce, + params.deadline + ) + ); + } } diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index d71faaa03..a9f0539f2 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -5,10 +5,10 @@ pragma solidity 0.8.28; 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 {IntentConsumer} from 'src/misc/IntentConsumer.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'; +import {EIP712Hash, EIP712Types} from 'src/position-manager/libraries/EIP712Hash.sol'; import {PercentageMath} from 'src/libraries/math/PercentageMath.sol'; import {WadRayMath} from 'src/libraries/math/WadRayMath.sol'; import {KeyValueList} from 'src/spoke/libraries/KeyValueList.sol'; @@ -16,7 +16,6 @@ import {LiquidationLogic} from 'src/spoke/libraries/LiquidationLogic.sol'; import {PositionStatusMap} from 'src/spoke/libraries/PositionStatusMap.sol'; import {ReserveFlags, ReserveFlagsMap} from 'src/spoke/libraries/ReserveFlagsMap.sol'; import {UserPositionDebt} from 'src/spoke/libraries/UserPositionDebt.sol'; -import {NoncesKeyed} from 'src/utils/NoncesKeyed.sol'; import {Multicall} from 'src/utils/Multicall.sol'; import {IAaveOracle} from 'src/spoke/interfaces/IAaveOracle.sol'; import {IHubBase} from 'src/hub/interfaces/IHubBase.sol'; @@ -26,7 +25,7 @@ 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, AccessManagedUpgradeable, IntentConsumer { using SafeCast for *; using SafeERC20 for IERC20; using MathUtils for *; @@ -37,13 +36,11 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea using PositionStatusMap for *; using ReserveFlagsMap for ReserveFlags; using UserPositionDebt for ISpoke.UserPosition; + using EIP712Hash for EIP712Types.SetUserPositionManager; /// @inheritdoc ISpoke bytes32 public constant SET_USER_POSITION_MANAGER_TYPEHASH = - // keccak256('SetUserPositionManager(address positionManager,address user,bool approve,uint256 nonce,uint256 deadline)') - 0x758d23a3c07218b7ea0b4f7f63903c4e9d5cbde72d3bcfe3e9896639025a0214; - - /// @inheritdoc ISpoke + EIP712Hash.SET_USER_POSITION_MANAGER_TYPEHASH; address public immutable ORACLE; /// @dev The maximum allowed value for an asset identifier (inclusive). @@ -448,29 +445,15 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea /// @inheritdoc ISpoke function setUserPositionManagerWithSig( - address positionManager, - address user, - bool approve, - uint256 nonce, - uint256 deadline, + EIP712Types.SetUserPositionManager calldata params, bytes calldata signature ) external { - require(block.timestamp <= deadline, InvalidSignature()); - bytes32 digest = _hashTypedData( - keccak256( - abi.encode( - SET_USER_POSITION_MANAGER_TYPEHASH, - positionManager, - user, - approve, - nonce, - deadline - ) - ) - ); - require(SignatureChecker.isValidSignatureNow(user, digest, signature), InvalidSignature()); - _useCheckedNonce(user, nonce); - _setUserPositionManager({positionManager: positionManager, user: user, approve: approve}); + _verifyAndConsumeIntent(params.user, params.hash(), params.nonce, params.deadline, signature); + _setUserPositionManager({ + positionManager: params.positionManager, + user: params.user, + approve: params.approve + }); } /// @inheritdoc ISpoke diff --git a/src/spoke/interfaces/ISpoke.sol b/src/spoke/interfaces/ISpoke.sol index 29a0a5819..7117cf2c2 100644 --- a/src/spoke/interfaces/ISpoke.sol +++ b/src/spoke/interfaces/ISpoke.sol @@ -7,6 +7,7 @@ import {INoncesKeyed} from 'src/interfaces/INoncesKeyed.sol'; import {IMulticall} from 'src/interfaces/IMulticall.sol'; import {IHubBase} from 'src/hub/interfaces/IHubBase.sol'; import {ISpokeBase} from 'src/spoke/interfaces/ISpokeBase.sol'; +import {EIP712Types} from 'src/libraries/types/EIP712Types.sol'; type ReserveFlags is uint8; @@ -270,9 +271,6 @@ interface ISpoke is ISpokeBase, IMulticall, INoncesKeyed, IAccessManaged { /// @notice Thrown if an inactive position manager is set as a user's position manager. error InactivePositionManager(); - /// @notice Thrown when a signature is invalid. - error InvalidSignature(); - /// @notice Thrown for an invalid zero address. error InvalidAddress(); @@ -403,18 +401,10 @@ interface ISpoke is ISpokeBase, IMulticall, INoncesKeyed, IAccessManaged { /// @notice Enables a user to grant or revoke approval for a position manager using an EIP712-typed intent. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. - /// @param positionManager The address of the position manager. - /// @param user The address of the user on whose behalf position manager can act. - /// @param approve True to approve the position manager, false to revoke approval. - /// @param nonce The key-prefixed nonce for the signature. - /// @param deadline The deadline for the signature. + /// @param params The structured setUserPositionManager parameters. /// @param signature The EIP712-compliant signature bytes. function setUserPositionManagerWithSig( - address positionManager, - address user, - bool approve, - uint256 nonce, - uint256 deadline, + EIP712Types.SetUserPositionManager calldata params, bytes calldata signature ) external; diff --git a/tests/Base.t.sol b/tests/Base.t.sol index 424bc3683..0f6458e59 100644 --- a/tests/Base.t.sol +++ b/tests/Base.t.sol @@ -38,6 +38,7 @@ import {Roles} from 'src/libraries/types/Roles.sol'; import {Rescuable, IRescuable} from 'src/utils/Rescuable.sol'; import {NoncesKeyed, INoncesKeyed} from 'src/utils/NoncesKeyed.sol'; import {UnitPriceFeed} from 'src/misc/UnitPriceFeed.sol'; +import {IntentConsumer} from 'src/misc/IntentConsumer.sol'; import {AccessManagerEnumerable} from 'src/access/AccessManagerEnumerable.sol'; // hub diff --git a/tests/gas/Spoke.Operations.gas.t.sol b/tests/gas/Spoke.Operations.gas.t.sol index aa6d2b8d9..de05ac476 100644 --- a/tests/gas/Spoke.Operations.gas.t.sol +++ b/tests/gas/Spoke.Operations.gas.t.sol @@ -309,14 +309,7 @@ contract SpokeOperations_Gas_Tests is SpokeBase { (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPk, _getTypedDataHash(spoke, params)); bytes memory signature = abi.encodePacked(r, s, v); - spoke.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke.setUserPositionManagerWithSig(params, signature); vm.snapshotGasLastCall(NAMESPACE, 'setUserPositionManagerWithSig: enable'); params.approve = false; @@ -324,14 +317,7 @@ contract SpokeOperations_Gas_Tests is SpokeBase { (v, r, s) = vm.sign(userPk, _getTypedDataHash(spoke, params)); signature = abi.encodePacked(r, s, v); - spoke.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke.setUserPositionManagerWithSig(params, signature); vm.snapshotGasLastCall(NAMESPACE, 'setUserPositionManagerWithSig: disable'); } diff --git a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol index 3ff896f34..f1eea656d 100644 --- a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol +++ b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol @@ -91,16 +91,9 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); bytes memory signature = abi.encodePacked(r, s, v); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke1.setUserPositionManagerWithSig(params, signature); } function test_setUserPositionManagerWithSig_revertsWith_InvalidSignature_dueTo_InvalidSigner() @@ -115,16 +108,9 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { (uint8 v, bytes32 r, bytes32 s) = vm.sign(randomUserPk, digest); bytes memory signature = abi.encodePacked(r, s, v); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke1.setUserPositionManagerWithSig(params, signature); } function test_setUserPositionManagerWithSig_revertsWith_InvalidAccountNonce(bytes32) public { @@ -148,14 +134,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { abi.encodeWithSelector(INoncesKeyed.InvalidAccountNonce.selector, params.user, currentNonce) ); vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke1.setUserPositionManagerWithSig(params, signature); } function test_setUserPositionManagerWithSig() public { @@ -173,14 +152,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { emit ISpoke.SetUserPositionManager(params.user, params.positionManager, params.approve); vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke1.setUserPositionManagerWithSig(params, signature); _assertNonceIncrement(spoke1, params.user, params.nonce); assertEq(spoke1.isPositionManager(params.user, params.positionManager), params.approve); @@ -203,16 +175,9 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); bytes memory signature = abi.encodePacked(r, s, v); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke1.setUserPositionManagerWithSig(params, signature); } function test_setUserPositionManagerWithSig_ERC1271_revertsWith_InvalidSignature_dueTo_InvalidHash() @@ -243,16 +208,11 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { vm.prank(alice); smartWallet.approveHash(digest); - vm.expectRevert(ISpoke.InvalidSignature.selector); + invalidParams.nonce = params.nonce; + + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - invalidParams.positionManager, - invalidParams.user, - invalidParams.approve, - params.nonce, - invalidParams.deadline, - signature - ); + spoke1.setUserPositionManagerWithSig(invalidParams, signature); } function test_setUserPositionManagerWithSig_ERC1271_revertsWith_InvalidAccountNonce( @@ -286,14 +246,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { ) ); vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke1.setUserPositionManagerWithSig(params, signature); } function test_setUserPositionManagerWithSig_ERC1271() public { @@ -322,14 +275,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { emit ISpoke.SetUserPositionManager(params.user, params.positionManager, params.approve); vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke1.setUserPositionManagerWithSig(params, signature); _assertNonceIncrement(spoke1, params.user, params.nonce); assertEq(spoke1.isPositionManager(params.user, params.positionManager), params.approve); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol index 2bf1c37a2..f0541797e 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol @@ -9,7 +9,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Supply memory p = _supplyData(spoke1, alice, _warpAfterRandomDeadline()); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.supplyWithSig(p, signature); } @@ -18,7 +18,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Withdraw memory p = _withdrawData(spoke1, alice, _warpAfterRandomDeadline()); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.withdrawWithSig(p, signature); } @@ -27,7 +27,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Borrow memory p = _borrowData(spoke1, alice, _warpAfterRandomDeadline()); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.borrowWithSig(p, signature); } @@ -36,7 +36,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Repay memory p = _repayData(spoke1, alice, _warpAfterRandomDeadline()); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.repayWithSig(p, signature); } @@ -48,7 +48,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.SetUsingAsCollateral memory p = _setAsCollateralData(spoke1, alice, deadline); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.setUsingAsCollateralWithSig(p, signature); } @@ -60,7 +60,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.UpdateUserRiskPremium memory p = _updateRiskPremiumData(spoke1, alice, deadline); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.updateUserRiskPremiumWithSig(p, signature); } @@ -75,7 +75,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.updateUserDynamicConfigWithSig(p, signature); } @@ -88,7 +88,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Supply memory p = _supplyData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.supplyWithSig(p, signature); } @@ -101,7 +101,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Withdraw memory p = _withdrawData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.withdrawWithSig(p, signature); } @@ -114,7 +114,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Borrow memory p = _borrowData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.borrowWithSig(p, signature); } @@ -127,7 +127,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Repay memory p = _repayData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.repayWithSig(p, signature); } @@ -143,7 +143,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.SetUsingAsCollateral memory p = _setAsCollateralData(spoke1, onBehalfOf, deadline); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.setUsingAsCollateralWithSig(p, signature); } @@ -159,7 +159,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.UpdateUserRiskPremium memory p = _updateRiskPremiumData(spoke1, user, deadline); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.updateUserRiskPremiumWithSig(p, signature); } @@ -175,7 +175,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.UpdateUserDynamicConfig memory p = _updateDynamicConfigData(spoke1, user, deadline); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(ISpoke.InvalidSignature.selector); + vm.expectRevert(IntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.updateUserDynamicConfigWithSig(p, signature); } From 7a56dae0723c5bc4e0fdb275c3b95ad06d1276b2 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:33:47 +0530 Subject: [PATCH 3/9] chore: use named args --- src/position-manager/SignatureGateway.sol | 56 ++++++++++++++++++++--- src/spoke/Spoke.sol | 8 +++- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index 5b37c6e1b..540eadab9 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -34,7 +34,13 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo address spoke = params.spoke; uint256 reserveId = params.reserveId; address user = params.onBehalfOf; - _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); + _verifyAndConsumeIntent({ + signer: user, + intentHash: params.hash(), + nonce: params.nonce, + deadline: params.deadline, + signature: signature + }); IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); underlying.safeTransferFrom(user, address(this), params.amount); @@ -52,7 +58,13 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo address spoke = params.spoke; uint256 reserveId = params.reserveId; address user = params.onBehalfOf; - _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); + _verifyAndConsumeIntent({ + signer: user, + intentHash: params.hash(), + nonce: params.nonce, + deadline: params.deadline, + signature: signature + }); IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); (uint256 withdrawnShares, uint256 withdrawnAmount) = ISpoke(spoke).withdraw( @@ -74,7 +86,13 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo address spoke = params.spoke; uint256 reserveId = params.reserveId; address user = params.onBehalfOf; - _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); + _verifyAndConsumeIntent({ + signer: user, + intentHash: params.hash(), + nonce: params.nonce, + deadline: params.deadline, + signature: signature + }); IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); (uint256 borrowedShares, uint256 borrowedAmount) = ISpoke(spoke).borrow( @@ -96,7 +114,13 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo address spoke = params.spoke; uint256 reserveId = params.reserveId; address user = params.onBehalfOf; - _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); + _verifyAndConsumeIntent({ + signer: user, + intentHash: params.hash(), + nonce: params.nonce, + deadline: params.deadline, + signature: signature + }); IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); uint256 repayAmount = MathUtils.min( @@ -116,7 +140,13 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo bytes calldata signature ) external onlyRegisteredSpoke(params.spoke) { address user = params.onBehalfOf; - _verifyAndConsumeIntent(user, params.hash(), params.nonce, params.deadline, signature); + _verifyAndConsumeIntent({ + signer: user, + intentHash: params.hash(), + nonce: params.nonce, + deadline: params.deadline, + signature: signature + }); ISpoke(params.spoke).setUsingAsCollateral(params.reserveId, params.useAsCollateral, user); } @@ -126,7 +156,13 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo EIP712Types.UpdateUserRiskPremium calldata params, bytes calldata signature ) external onlyRegisteredSpoke(params.spoke) { - _verifyAndConsumeIntent(params.user, params.hash(), params.nonce, params.deadline, signature); + _verifyAndConsumeIntent({ + signer: params.user, + intentHash: params.hash(), + nonce: params.nonce, + deadline: params.deadline, + signature: signature + }); ISpoke(params.spoke).updateUserRiskPremium(params.user); } @@ -136,7 +172,13 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo EIP712Types.UpdateUserDynamicConfig calldata params, bytes calldata signature ) external onlyRegisteredSpoke(params.spoke) { - _verifyAndConsumeIntent(params.user, params.hash(), params.nonce, params.deadline, signature); + _verifyAndConsumeIntent({ + signer: params.user, + intentHash: params.hash(), + nonce: params.nonce, + deadline: params.deadline, + signature: signature + }); ISpoke(params.spoke).updateUserDynamicConfig(params.user); } diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index a9f0539f2..4a5b46f6d 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -448,7 +448,13 @@ abstract contract Spoke is ISpoke, Multicall, AccessManagedUpgradeable, IntentCo EIP712Types.SetUserPositionManager calldata params, bytes calldata signature ) external { - _verifyAndConsumeIntent(params.user, params.hash(), params.nonce, params.deadline, signature); + _verifyAndConsumeIntent({ + signer: params.user, + intentHash: params.hash(), + nonce: params.nonce, + deadline: params.deadline, + signature: signature + }); _setUserPositionManager({ positionManager: params.positionManager, user: params.user, From 6270124aebd95e8e016106b63c96b025840e0474 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:36:01 +0530 Subject: [PATCH 4/9] chore: get code size under control --- src/spoke/Spoke.sol | 1 + src/spoke/instances/SpokeInstance.sol | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index 4a5b46f6d..fd7e309df 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -41,6 +41,7 @@ abstract contract Spoke is ISpoke, Multicall, AccessManagedUpgradeable, IntentCo /// @inheritdoc ISpoke bytes32 public constant SET_USER_POSITION_MANAGER_TYPEHASH = EIP712Hash.SET_USER_POSITION_MANAGER_TYPEHASH; + address public immutable ORACLE; /// @dev The maximum allowed value for an asset identifier (inclusive). 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 ba3bf554088b0aeeea1202177a0a538ba43c8808 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:36:53 +0530 Subject: [PATCH 5/9] feat: codesize --- src/spoke/Spoke.sol | 4 ---- src/spoke/instances/SpokeInstance.sol | 2 +- src/spoke/interfaces/ISpoke.sol | 4 ---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index fd7e309df..4e04a0fab 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -38,10 +38,6 @@ abstract contract Spoke is ISpoke, Multicall, AccessManagedUpgradeable, IntentCo using UserPositionDebt for ISpoke.UserPosition; using EIP712Hash for EIP712Types.SetUserPositionManager; - /// @inheritdoc ISpoke - bytes32 public constant SET_USER_POSITION_MANAGER_TYPEHASH = - EIP712Hash.SET_USER_POSITION_MANAGER_TYPEHASH; - address public immutable ORACLE; /// @dev The maximum allowed value for an asset identifier (inclusive). diff --git a/src/spoke/instances/SpokeInstance.sol b/src/spoke/instances/SpokeInstance.sol index 60c65ab1d..69a869dc9 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 internal constant SPOKE_REVISION = 1; + uint64 public 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. diff --git a/src/spoke/interfaces/ISpoke.sol b/src/spoke/interfaces/ISpoke.sol index 7117cf2c2..0d500bf38 100644 --- a/src/spoke/interfaces/ISpoke.sol +++ b/src/spoke/interfaces/ISpoke.sol @@ -518,10 +518,6 @@ interface ISpoke is ISpokeBase, IMulticall, INoncesKeyed, IAccessManaged { /// @return The address of the library. function getLiquidationLogic() external pure returns (address); - /// @notice Returns the type hash for the SetUserPositionManager intent. - /// @return The bytes-encoded EIP-712 struct hash representing the intent. - function SET_USER_POSITION_MANAGER_TYPEHASH() external view returns (bytes32); - /// @notice Returns the address of the AaveOracle contract. function ORACLE() external view returns (address); } From ee8544b5e044e97fc463cd587117224408f9a6b5 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:08:42 +0530 Subject: [PATCH 6/9] chore: snapshot --- snapshots/NativeTokenGateway.Operations.json | 12 ++--- snapshots/SignatureGateway.Operations.json | 14 ++--- snapshots/Spoke.Getters.json | 10 ++-- .../Spoke.Operations.ZeroRiskPremium.json | 54 +++++++++---------- snapshots/Spoke.Operations.json | 54 +++++++++---------- 5 files changed, 72 insertions(+), 72 deletions(-) diff --git a/snapshots/NativeTokenGateway.Operations.json b/snapshots/NativeTokenGateway.Operations.json index 51f656579..2daa564a2 100644 --- a/snapshots/NativeTokenGateway.Operations.json +++ b/snapshots/NativeTokenGateway.Operations.json @@ -1,8 +1,8 @@ { - "borrowNative": "229294", - "repayNative": "168089", - "supplyAsCollateralNative": "160373", - "supplyNative": "136476", - "withdrawNative: full": "125620", - "withdrawNative: partial": "136825" + "borrowNative": "229272", + "repayNative": "168023", + "supplyAsCollateralNative": "160307", + "supplyNative": "136441", + "withdrawNative: full": "125585", + "withdrawNative: partial": "136781" } \ No newline at end of file diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index 3ed8cda2f..0e165c442 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { - "borrowWithSig": "215593", - "repayWithSig": "188947", + "borrowWithSig": "215571", + "repayWithSig": "188881", "setSelfAsUserPositionManagerWithSig": "76464", - "setUsingAsCollateralWithSig": "85107", - "supplyWithSig": "153186", - "updateUserDynamicConfigWithSig": "62662", - "updateUserRiskPremiumWithSig": "61472", - "withdrawWithSig": "131704" + "setUsingAsCollateralWithSig": "85085", + "supplyWithSig": "153151", + "updateUserDynamicConfigWithSig": "62750", + "updateUserRiskPremiumWithSig": "61450", + "withdrawWithSig": "131668" } \ No newline at end of file diff --git a/snapshots/Spoke.Getters.json b/snapshots/Spoke.Getters.json index aead49eba..000034236 100644 --- a/snapshots/Spoke.Getters.json +++ b/snapshots/Spoke.Getters.json @@ -1,7 +1,7 @@ { - "getUserAccountData: supplies: 0, borrows: 0": "11915", - "getUserAccountData: supplies: 1, borrows: 0": "48578", - "getUserAccountData: supplies: 2, borrows: 0": "80356", - "getUserAccountData: supplies: 2, borrows: 1": "100144", - "getUserAccountData: supplies: 2, borrows: 2": "118574" + "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" } \ No newline at end of file diff --git a/snapshots/Spoke.Operations.ZeroRiskPremium.json b/snapshots/Spoke.Operations.ZeroRiskPremium.json index 167c3e687..d7092e5f5 100644 --- a/snapshots/Spoke.Operations.ZeroRiskPremium.json +++ b/snapshots/Spoke.Operations.ZeroRiskPremium.json @@ -1,33 +1,33 @@ { "borrow: first": "191303", "borrow: second action, same reserve": "171275", - "liquidationCall (receiveShares): full": "300081", - "liquidationCall (receiveShares): partial": "299799", - "liquidationCall: full": "310446", - "liquidationCall: partial": "310164", - "permitReserve + repay (multicall)": "166072", - "permitReserve + supply (multicall)": "146840", - "permitReserve + supply + enable collateral (multicall)": "160551", - "repay: full": "126159", - "repay: partial": "131048", + "liquidationCall (receiveShares): full": "300103", + "liquidationCall (receiveShares): partial": "299821", + "liquidationCall: full": "310468", + "liquidationCall: partial": "310186", + "permitReserve + repay (multicall)": "166006", + "permitReserve + supply (multicall)": "146774", + "permitReserve + supply + enable collateral (multicall)": "160463", + "repay: full": "126137", + "repay: partial": "131026", "setUserPositionManagerWithSig: disable": "45813", "setUserPositionManagerWithSig: enable": "69842", - "supply + enable collateral (multicall)": "140602", - "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)": "140536", + "supply: 0 borrows, collateral disabled": "123657", + "supply: 0 borrows, collateral enabled": "106579", + "supply: second action, same reserve": "106557", + "updateUserDynamicConfig: 1 collateral": "73782", + "updateUserDynamicConfig: 2 collaterals": "88639", + "updateUserRiskPremium: 1 borrow": "94782", + "updateUserRiskPremium: 2 borrows": "104597", + "usingAsCollateral: 0 borrows, enable": "58893", + "usingAsCollateral: 1 borrow, disable": "105050", + "usingAsCollateral: 1 borrow, enable": "41781", + "usingAsCollateral: 2 borrows, disable": "126033", + "usingAsCollateral: 2 borrows, enable": "41793", + "withdraw: 0 borrows, full": "128888", + "withdraw: 0 borrows, partial": "133451", + "withdraw: 1 borrow, partial": "161014", + "withdraw: 2 borrows, partial": "174192", + "withdraw: non collateral": "106522" } \ No newline at end of file diff --git a/snapshots/Spoke.Operations.json b/snapshots/Spoke.Operations.json index 266a0a08c..581a05f26 100644 --- a/snapshots/Spoke.Operations.json +++ b/snapshots/Spoke.Operations.json @@ -1,33 +1,33 @@ { "borrow: first": "261699", "borrow: second action, same reserve": "204671", - "liquidationCall (receiveShares): full": "333644", - "liquidationCall (receiveShares): partial": "333362", - "liquidationCall: full": "344009", - "liquidationCall: partial": "343727", - "permitReserve + repay (multicall)": "163308", - "permitReserve + supply (multicall)": "146840", - "permitReserve + supply + enable collateral (multicall)": "160551", - "repay: full": "120321", - "repay: partial": "139610", + "liquidationCall (receiveShares): full": "333666", + "liquidationCall (receiveShares): partial": "333384", + "liquidationCall: full": "344031", + "liquidationCall: partial": "343749", + "permitReserve + repay (multicall)": "163255", + "permitReserve + supply (multicall)": "146774", + "permitReserve + supply + enable collateral (multicall)": "160463", + "repay: full": "120299", + "repay: partial": "139588", "setUserPositionManagerWithSig: disable": "45813", "setUserPositionManagerWithSig: enable": "69842", - "supply + enable collateral (multicall)": "140602", - "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)": "140536", + "supply: 0 borrows, collateral disabled": "123657", + "supply: 0 borrows, collateral enabled": "106579", + "supply: second action, same reserve": "106557", + "updateUserDynamicConfig: 1 collateral": "73782", + "updateUserDynamicConfig: 2 collaterals": "88639", + "updateUserRiskPremium: 1 borrow": "151058", + "updateUserRiskPremium: 2 borrows": "204254", + "usingAsCollateral: 0 borrows, enable": "58893", + "usingAsCollateral: 1 borrow, disable": "161326", + "usingAsCollateral: 1 borrow, enable": "41781", + "usingAsCollateral: 2 borrows, disable": "233690", + "usingAsCollateral: 2 borrows, enable": "41793", + "withdraw: 0 borrows, full": "128888", + "withdraw: 0 borrows, partial": "133451", + "withdraw: 1 borrow, partial": "214788", + "withdraw: 2 borrows, partial": "259250", + "withdraw: non collateral": "106522" } \ No newline at end of file From 7ecb2b9000c00263b20d6498539759cf1549a2f9 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 16 Dec 2025 08:36:34 +0530 Subject: [PATCH 7/9] chore: abstract common getter --- src/interfaces/IIntentConsumer.sol | 16 +++++++++++ src/misc/IntentConsumer.sol | 13 +++++---- src/position-manager/SignatureGateway.sol | 5 ---- .../interfaces/ISignatureGateway.sol | 7 ++--- src/spoke/Spoke.sol | 5 ---- src/spoke/interfaces/ISpoke.sol | 7 ++--- tests/Base.t.sol | 2 +- .../Spoke.SetUserPositionManagerWithSig.t.sol | 8 +++--- ...tureGateway.Reverts.InvalidSignature.t.sol | 28 +++++++++---------- 9 files changed, 47 insertions(+), 44 deletions(-) create mode 100644 src/interfaces/IIntentConsumer.sol diff --git a/src/interfaces/IIntentConsumer.sol b/src/interfaces/IIntentConsumer.sol new file mode 100644 index 000000000..a7cf83cf9 --- /dev/null +++ b/src/interfaces/IIntentConsumer.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +// Copyright (c) 2025 Aave Labs +pragma solidity ^0.8.0; + +import {INoncesKeyed} from 'src/interfaces/INoncesKeyed.sol'; + +/// @title IIntentConsumer +/// @author Aave Labs +/// @notice Minimal interface for IntentConsumer. +interface IIntentConsumer is INoncesKeyed { + /// @notice Thrown when signature deadline has passed or signer is not the expected one. + error InvalidSignature(); + + /// @notice Returns the EIP-712 domain separator. + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/src/misc/IntentConsumer.sol b/src/misc/IntentConsumer.sol index 6c80429b6..a987adfa6 100644 --- a/src/misc/IntentConsumer.sol +++ b/src/misc/IntentConsumer.sol @@ -2,14 +2,12 @@ // Copyright (c) 2025 Aave Labs pragma solidity 0.8.28; -import {SignatureChecker} from 'src/dependencies/openzeppelin/SignatureChecker.sol'; import {EIP712} from 'src/dependencies/solady/EIP712.sol'; import {NoncesKeyed} from 'src/utils/NoncesKeyed.sol'; +import {SignatureChecker} from 'src/dependencies/openzeppelin/SignatureChecker.sol'; +import {IIntentConsumer} from 'src/interfaces/IIntentConsumer.sol'; -abstract contract IntentConsumer is NoncesKeyed, EIP712 { - /// @notice Thrown when signature deadline has passed or signer is not `onBehalfOf`. - error InvalidSignature(); - +abstract contract IntentConsumer is IIntentConsumer, NoncesKeyed, EIP712 { /// @dev Verifies the signature for given signer & intent hash, and consumes the keyed-nonce. /// @param signer The address of the user. /// @param intentHash The hash of the intent struct. @@ -28,4 +26,9 @@ abstract contract IntentConsumer is NoncesKeyed, EIP712 { require(SignatureChecker.isValidSignatureNow(signer, digest, signature), InvalidSignature()); _useCheckedNonce(signer, nonce); } + + /// @inheritdoc IIntentConsumer + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _domainSeparator(); + } } diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index 540eadab9..5e54100fd 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -218,11 +218,6 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo {} catch {} } - /// @inheritdoc ISignatureGateway - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _domainSeparator(); - } - /// @inheritdoc ISignatureGateway function SUPPLY_TYPEHASH() external pure returns (bytes32) { return EIP712Hash.SUPPLY_TYPEHASH; diff --git a/src/position-manager/interfaces/ISignatureGateway.sol b/src/position-manager/interfaces/ISignatureGateway.sol index 6074f3fc8..4ecd08eb6 100644 --- a/src/position-manager/interfaces/ISignatureGateway.sol +++ b/src/position-manager/interfaces/ISignatureGateway.sol @@ -3,14 +3,14 @@ pragma solidity ^0.8.0; import {IMulticall} from 'src/interfaces/IMulticall.sol'; -import {INoncesKeyed} from 'src/interfaces/INoncesKeyed.sol'; +import {IIntentConsumer} from 'src/interfaces/IIntentConsumer.sol'; import {EIP712Types} from 'src/libraries/types/EIP712Types.sol'; import {IGatewayBase} from 'src/position-manager/interfaces/IGatewayBase.sol'; /// @title ISignatureGateway /// @author Aave Labs /// @notice Minimal interface for protocol actions involving signed intents. -interface ISignatureGateway is IMulticall, INoncesKeyed, IGatewayBase { +interface ISignatureGateway is IMulticall, IIntentConsumer, IGatewayBase { /// @notice Facilitates `supply` action on the specified registered `spoke` with a typed signature from `onBehalfOf`. /// @dev Supplied assets are pulled from `onBehalfOf`, prior approval to this gateway is required. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. @@ -120,9 +120,6 @@ interface ISignatureGateway is IMulticall, INoncesKeyed, IGatewayBase { bytes32 permitS ) external; - /// @notice Returns the EIP712 domain separator. - function DOMAIN_SEPARATOR() external view returns (bytes32); - /// @notice Returns the type hash for the Supply intent. function SUPPLY_TYPEHASH() external view returns (bytes32); diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index 4e04a0fab..4bb431ce4 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -660,11 +660,6 @@ abstract contract Spoke is ISpoke, Multicall, AccessManagedUpgradeable, IntentCo return _isPositionManager(user, positionManager); } - /// @inheritdoc ISpoke - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _domainSeparator(); - } - /// @inheritdoc ISpoke function getLiquidationLogic() external pure returns (address) { return address(LiquidationLogic); diff --git a/src/spoke/interfaces/ISpoke.sol b/src/spoke/interfaces/ISpoke.sol index 0d500bf38..a16cfba89 100644 --- a/src/spoke/interfaces/ISpoke.sol +++ b/src/spoke/interfaces/ISpoke.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import {IAccessManaged} from 'src/dependencies/openzeppelin/IAccessManaged.sol'; -import {INoncesKeyed} from 'src/interfaces/INoncesKeyed.sol'; +import {IIntentConsumer} from 'src/interfaces/IIntentConsumer.sol'; import {IMulticall} from 'src/interfaces/IMulticall.sol'; import {IHubBase} from 'src/hub/interfaces/IHubBase.sol'; import {ISpokeBase} from 'src/spoke/interfaces/ISpokeBase.sol'; @@ -14,7 +14,7 @@ type ReserveFlags is uint8; /// @title ISpoke /// @author Aave Labs /// @notice Full interface for Spoke. -interface ISpoke is ISpokeBase, IMulticall, INoncesKeyed, IAccessManaged { +interface ISpoke is ISpokeBase, IMulticall, IIntentConsumer, IAccessManaged { /// @notice Reserve level data. /// @dev underlying The address of the underlying asset. /// @dev hub The address of the associated Hub. @@ -511,9 +511,6 @@ interface ISpoke is ISpokeBase, IMulticall, INoncesKeyed, IAccessManaged { /// @return True if positionManager is active and approved by user. function isPositionManager(address user, address positionManager) external view returns (bool); - /// @notice Returns the EIP-712 domain separator. - function DOMAIN_SEPARATOR() external view returns (bytes32); - /// @notice Returns the address of the external `LiquidationLogic` library. /// @return The address of the library. function getLiquidationLogic() external pure returns (address); diff --git a/tests/Base.t.sol b/tests/Base.t.sol index 0f6458e59..ffee1b789 100644 --- a/tests/Base.t.sol +++ b/tests/Base.t.sol @@ -38,7 +38,7 @@ import {Roles} from 'src/libraries/types/Roles.sol'; import {Rescuable, IRescuable} from 'src/utils/Rescuable.sol'; import {NoncesKeyed, INoncesKeyed} from 'src/utils/NoncesKeyed.sol'; import {UnitPriceFeed} from 'src/misc/UnitPriceFeed.sol'; -import {IntentConsumer} from 'src/misc/IntentConsumer.sol'; +import {IntentConsumer, IIntentConsumer} from 'src/misc/IntentConsumer.sol'; import {AccessManagerEnumerable} from 'src/access/AccessManagerEnumerable.sol'; // hub diff --git a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol index f1eea656d..18c6b447c 100644 --- a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol +++ b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol @@ -91,7 +91,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); bytes memory signature = abi.encodePacked(r, s, v); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); spoke1.setUserPositionManagerWithSig(params, signature); } @@ -108,7 +108,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { (uint8 v, bytes32 r, bytes32 s) = vm.sign(randomUserPk, digest); bytes memory signature = abi.encodePacked(r, s, v); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); spoke1.setUserPositionManagerWithSig(params, signature); } @@ -175,7 +175,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, digest); bytes memory signature = abi.encodePacked(r, s, v); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); spoke1.setUserPositionManagerWithSig(params, signature); } @@ -210,7 +210,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { invalidParams.nonce = params.nonce; - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); spoke1.setUserPositionManagerWithSig(invalidParams, signature); } diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol index f0541797e..17e087621 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol @@ -9,7 +9,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Supply memory p = _supplyData(spoke1, alice, _warpAfterRandomDeadline()); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.supplyWithSig(p, signature); } @@ -18,7 +18,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Withdraw memory p = _withdrawData(spoke1, alice, _warpAfterRandomDeadline()); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.withdrawWithSig(p, signature); } @@ -27,7 +27,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Borrow memory p = _borrowData(spoke1, alice, _warpAfterRandomDeadline()); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.borrowWithSig(p, signature); } @@ -36,7 +36,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Repay memory p = _repayData(spoke1, alice, _warpAfterRandomDeadline()); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.repayWithSig(p, signature); } @@ -48,7 +48,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.SetUsingAsCollateral memory p = _setAsCollateralData(spoke1, alice, deadline); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.setUsingAsCollateralWithSig(p, signature); } @@ -60,7 +60,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.UpdateUserRiskPremium memory p = _updateRiskPremiumData(spoke1, alice, deadline); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.updateUserRiskPremiumWithSig(p, signature); } @@ -75,7 +75,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.updateUserDynamicConfigWithSig(p, signature); } @@ -88,7 +88,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Supply memory p = _supplyData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.supplyWithSig(p, signature); } @@ -101,7 +101,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Withdraw memory p = _withdrawData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.withdrawWithSig(p, signature); } @@ -114,7 +114,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Borrow memory p = _borrowData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.borrowWithSig(p, signature); } @@ -127,7 +127,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.Repay memory p = _repayData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.repayWithSig(p, signature); } @@ -143,7 +143,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.SetUsingAsCollateral memory p = _setAsCollateralData(spoke1, onBehalfOf, deadline); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.setUsingAsCollateralWithSig(p, signature); } @@ -159,7 +159,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.UpdateUserRiskPremium memory p = _updateRiskPremiumData(spoke1, user, deadline); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.updateUserRiskPremiumWithSig(p, signature); } @@ -175,7 +175,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { EIP712Types.UpdateUserDynamicConfig memory p = _updateDynamicConfigData(spoke1, user, deadline); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); - vm.expectRevert(IntentConsumer.InvalidSignature.selector); + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); gateway.updateUserDynamicConfigWithSig(p, signature); } From f2cd686e769f0dc20865aa99a5f69b966f8b11e1 Mon Sep 17 00:00:00 2001 From: Kogaroshi <25688223+Kogaroshi@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:24:55 +0100 Subject: [PATCH 8/9] rft : move EIP712Hash to general libs --- snapshots/NativeTokenGateway.Operations.json | 12 ++--- snapshots/SignatureGateway.Operations.json | 14 ++--- snapshots/Spoke.Getters.json | 10 ++-- .../Spoke.Operations.ZeroRiskPremium.json | 54 +++++++++---------- snapshots/Spoke.Operations.json | 54 +++++++++---------- .../cryptography}/EIP712Hash.sol | 0 src/position-manager/SignatureGateway.sol | 2 +- src/spoke/Spoke.sol | 7 ++- src/spoke/interfaces/ISpoke.sol | 3 ++ .../Spoke.SetUserPositionManagerWithSig.t.sol | 6 ++- .../unit/{misc => libraries}/EIP712Hash.t.sol | 2 +- 11 files changed, 88 insertions(+), 76 deletions(-) rename src/{position-manager/libraries => libraries/cryptography}/EIP712Hash.sol (100%) rename tests/unit/{misc => libraries}/EIP712Hash.t.sol (98%) diff --git a/snapshots/NativeTokenGateway.Operations.json b/snapshots/NativeTokenGateway.Operations.json index d60a06971..5c31554b6 100644 --- a/snapshots/NativeTokenGateway.Operations.json +++ b/snapshots/NativeTokenGateway.Operations.json @@ -1,8 +1,8 @@ { - "borrowNative": "229560", - "repayNative": "168311", - "supplyAsCollateralNative": "160307", - "supplyNative": "136441", - "withdrawNative: full": "125585", - "withdrawNative: partial": "136781" + "borrowNative": "229582", + "repayNative": "168377", + "supplyAsCollateralNative": "160373", + "supplyNative": "136476", + "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 0815d6e98..8a71aacb9 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { - "borrowWithSig": "215937", - "repayWithSig": "189247", + "borrowWithSig": "215959", + "repayWithSig": "189313", "setSelfAsUserPositionManagerWithSig": "75975", - "setUsingAsCollateralWithSig": "85085", - "supplyWithSig": "153213", - "updateUserDynamicConfigWithSig": "62750", - "updateUserRiskPremiumWithSig": "61450", - "withdrawWithSig": "131748" + "setUsingAsCollateralWithSig": "85107", + "supplyWithSig": "153248", + "updateUserDynamicConfigWithSig": "62662", + "updateUserRiskPremiumWithSig": "61472", + "withdrawWithSig": "131784" } \ No newline at end of file diff --git a/snapshots/Spoke.Getters.json b/snapshots/Spoke.Getters.json index 000034236..aead49eba 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": "11915", + "getUserAccountData: supplies: 1, borrows: 0": "48578", + "getUserAccountData: supplies: 2, borrows: 0": "80356", + "getUserAccountData: supplies: 2, borrows: 1": "100144", + "getUserAccountData: supplies: 2, borrows: 2": "118574" } \ No newline at end of file diff --git a/snapshots/Spoke.Operations.ZeroRiskPremium.json b/snapshots/Spoke.Operations.ZeroRiskPremium.json index 04498db12..2d1a410ee 100644 --- a/snapshots/Spoke.Operations.ZeroRiskPremium.json +++ b/snapshots/Spoke.Operations.ZeroRiskPremium.json @@ -1,33 +1,33 @@ { "borrow: first": "191303", "borrow: second action, same reserve": "171275", - "liquidationCall (receiveShares): full": "300391", - "liquidationCall (receiveShares): partial": "300109", - "liquidationCall: full": "310756", - "liquidationCall: partial": "310474", - "permitReserve + repay (multicall)": "166294", - "permitReserve + supply (multicall)": "146774", - "permitReserve + supply + enable collateral (multicall)": "160463", - "repay: full": "126425", - "repay: partial": "131314", + "liquidationCall (receiveShares): full": "300369", + "liquidationCall (receiveShares): partial": "300087", + "liquidationCall: full": "310734", + "liquidationCall: partial": "310452", + "permitReserve + repay (multicall)": "166360", + "permitReserve + supply (multicall)": "146840", + "permitReserve + supply + enable collateral (multicall)": "160551", + "repay: full": "126447", + "repay: partial": "131336", "setUserPositionManagerWithSig: disable": "45813", "setUserPositionManagerWithSig: enable": "69842", - "supply + enable collateral (multicall)": "140536", - "supply: 0 borrows, collateral disabled": "123657", - "supply: 0 borrows, collateral enabled": "106579", - "supply: second action, same reserve": "106557", - "updateUserDynamicConfig: 1 collateral": "73782", - "updateUserDynamicConfig: 2 collaterals": "88639", - "updateUserRiskPremium: 1 borrow": "94782", - "updateUserRiskPremium: 2 borrows": "104597", - "usingAsCollateral: 0 borrows, enable": "58893", - "usingAsCollateral: 1 borrow, disable": "105050", - "usingAsCollateral: 1 borrow, enable": "41781", - "usingAsCollateral: 2 borrows, disable": "126033", - "usingAsCollateral: 2 borrows, enable": "41793", - "withdraw: 0 borrows, full": "128888", - "withdraw: 0 borrows, partial": "133451", - "withdraw: 1 borrow, partial": "161014", - "withdraw: 2 borrows, partial": "174192", - "withdraw: non collateral": "106522" + "supply + enable collateral (multicall)": "140602", + "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" } \ No newline at end of file diff --git a/snapshots/Spoke.Operations.json b/snapshots/Spoke.Operations.json index 7ebc3d9d0..6a2a310e6 100644 --- a/snapshots/Spoke.Operations.json +++ b/snapshots/Spoke.Operations.json @@ -1,33 +1,33 @@ { "borrow: first": "261987", "borrow: second action, same reserve": "204959", - "liquidationCall (receiveShares): full": "334242", - "liquidationCall (receiveShares): partial": "333960", - "liquidationCall: full": "344607", - "liquidationCall: partial": "344325", - "permitReserve + repay (multicall)": "163485", - "permitReserve + supply (multicall)": "146774", - "permitReserve + supply + enable collateral (multicall)": "160463", - "repay: full": "120587", - "repay: partial": "139876", + "liquidationCall (receiveShares): full": "334220", + "liquidationCall (receiveShares): partial": "333938", + "liquidationCall: full": "344585", + "liquidationCall: partial": "344303", + "permitReserve + repay (multicall)": "163538", + "permitReserve + supply (multicall)": "146840", + "permitReserve + supply + enable collateral (multicall)": "160551", + "repay: full": "120609", + "repay: partial": "139898", "setUserPositionManagerWithSig: disable": "45813", "setUserPositionManagerWithSig: enable": "69842", - "supply + enable collateral (multicall)": "140536", - "supply: 0 borrows, collateral disabled": "123657", - "supply: 0 borrows, collateral enabled": "106579", - "supply: second action, same reserve": "106557", - "updateUserDynamicConfig: 1 collateral": "73782", - "updateUserDynamicConfig: 2 collaterals": "88639", - "updateUserRiskPremium: 1 borrow": "151346", - "updateUserRiskPremium: 2 borrows": "204830", - "usingAsCollateral: 0 borrows, enable": "58893", - "usingAsCollateral: 1 borrow, disable": "161614", - "usingAsCollateral: 1 borrow, enable": "41781", - "usingAsCollateral: 2 borrows, disable": "234266", - "usingAsCollateral: 2 borrows, enable": "41793", - "withdraw: 0 borrows, full": "128888", - "withdraw: 0 borrows, partial": "133451", - "withdraw: 1 borrow, partial": "215076", - "withdraw: 2 borrows, partial": "259826", - "withdraw: non collateral": "106522" + "supply + enable collateral (multicall)": "140602", + "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": "151368", + "updateUserRiskPremium: 2 borrows": "204852", + "usingAsCollateral: 0 borrows, enable": "58915", + "usingAsCollateral: 1 borrow, disable": "161636", + "usingAsCollateral: 1 borrow, enable": "41803", + "usingAsCollateral: 2 borrows, disable": "234288", + "usingAsCollateral: 2 borrows, enable": "41815", + "withdraw: 0 borrows, full": "128910", + "withdraw: 0 borrows, partial": "133473", + "withdraw: 1 borrow, partial": "215098", + "withdraw: 2 borrows, partial": "259848", + "withdraw: non collateral": "106544" } \ No newline at end of file diff --git a/src/position-manager/libraries/EIP712Hash.sol b/src/libraries/cryptography/EIP712Hash.sol similarity index 100% rename from src/position-manager/libraries/EIP712Hash.sol rename to src/libraries/cryptography/EIP712Hash.sol diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index da33e7665..0a611b4ee 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -7,7 +7,7 @@ import {SafeERC20, IERC20} from 'src/dependencies/openzeppelin/SafeERC20.sol'; import {IERC20Permit} from 'src/dependencies/openzeppelin/IERC20Permit.sol'; import {MathUtils} from 'src/libraries/math/MathUtils.sol'; import {Multicall} from 'src/utils/Multicall.sol'; -import {EIP712Hash, EIP712Types} from 'src/position-manager/libraries/EIP712Hash.sol'; +import {EIP712Hash, EIP712Types} from 'src/libraries/cryptography/EIP712Hash.sol'; import {GatewayBase} from 'src/position-manager/GatewayBase.sol'; import {ISpoke} from 'src/spoke/interfaces/ISpoke.sol'; import {ISignatureGateway} from 'src/position-manager/interfaces/ISignatureGateway.sol'; diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index 4bb431ce4..eb4bc86d2 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -8,7 +8,7 @@ import {IERC20Permit} from 'src/dependencies/openzeppelin/IERC20Permit.sol'; import {IntentConsumer} from 'src/misc/IntentConsumer.sol'; import {AccessManagedUpgradeable} from 'src/dependencies/openzeppelin-upgradeable/AccessManagedUpgradeable.sol'; import {MathUtils} from 'src/libraries/math/MathUtils.sol'; -import {EIP712Hash, EIP712Types} from 'src/position-manager/libraries/EIP712Hash.sol'; +import {EIP712Hash, EIP712Types} from 'src/libraries/cryptography/EIP712Hash.sol'; import {PercentageMath} from 'src/libraries/math/PercentageMath.sol'; import {WadRayMath} from 'src/libraries/math/WadRayMath.sol'; import {KeyValueList} from 'src/spoke/libraries/KeyValueList.sol'; @@ -660,6 +660,11 @@ abstract contract Spoke is ISpoke, Multicall, AccessManagedUpgradeable, IntentCo return _isPositionManager(user, positionManager); } + /// @inheritdoc ISpoke + function SET_USER_POSITION_MANAGER_TYPEHASH() external pure returns (bytes32) { + return EIP712Hash.SET_USER_POSITION_MANAGER_TYPEHASH; + } + /// @inheritdoc ISpoke function getLiquidationLogic() external pure returns (address) { return address(LiquidationLogic); diff --git a/src/spoke/interfaces/ISpoke.sol b/src/spoke/interfaces/ISpoke.sol index a16cfba89..e39319337 100644 --- a/src/spoke/interfaces/ISpoke.sol +++ b/src/spoke/interfaces/ISpoke.sol @@ -517,4 +517,7 @@ interface ISpoke is ISpokeBase, IMulticall, IIntentConsumer, IAccessManaged { /// @notice Returns the address of the AaveOracle contract. function ORACLE() external view returns (address); + + /// @notice Returns the type hash for the SetUserPositionManager intent. + function SET_USER_POSITION_MANAGER_TYPEHASH() external view returns (bytes32); } diff --git a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol index 18c6b447c..bf32e1773 100644 --- a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol +++ b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol @@ -66,7 +66,11 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { assertEq(spoke.DOMAIN_SEPARATOR(), expectedDomainSeparator); } - function test_setUserPositionManager_typeHash() public pure { + function test_setUserPositionManager_typeHash() public view { + assertEq( + Constants.SET_USER_POSITION_MANAGER_TYPEHASH, + spoke1.SET_USER_POSITION_MANAGER_TYPEHASH() + ); assertEq( Constants.SET_USER_POSITION_MANAGER_TYPEHASH, vm.eip712HashType('SetUserPositionManager') diff --git a/tests/unit/misc/EIP712Hash.t.sol b/tests/unit/libraries/EIP712Hash.t.sol similarity index 98% rename from tests/unit/misc/EIP712Hash.t.sol rename to tests/unit/libraries/EIP712Hash.t.sol index 2a5b29148..7796a1f67 100644 --- a/tests/unit/misc/EIP712Hash.t.sol +++ b/tests/unit/libraries/EIP712Hash.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import {Test} from 'forge-std/Test.sol'; -import {EIP712Hash, EIP712Types} from 'src/position-manager/libraries/EIP712Hash.sol'; +import {EIP712Hash, EIP712Types} from 'src/libraries/cryptography/EIP712Hash.sol'; contract EIP712HashTest is Test { using EIP712Hash for *; From 066ccbabdd98c80af8ff8e17a90c50bc8b44630e Mon Sep 17 00:00:00 2001 From: Kogaroshi <25688223+Kogaroshi@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:53:31 +0100 Subject: [PATCH 9/9] fix : setSelfAsUserPositionManagerWithSig consistency --- snapshots/SignatureGateway.Operations.json | 4 +- src/position-manager/SignatureGateway.sol | 19 +---- .../interfaces/ISignatureGateway.sol | 13 ++- tests/gas/Gateways.Operations.gas.t.sol | 5 +- ....SetSelfAsUserPositionManagerWithSig.t.sol | 81 ++++++++++++++----- .../SignatureGateway/SignatureGateway.t.sol | 5 +- 6 files changed, 75 insertions(+), 52 deletions(-) diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index 8a71aacb9..eeb9bb186 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { "borrowWithSig": "215959", "repayWithSig": "189313", - "setSelfAsUserPositionManagerWithSig": "75975", + "setSelfAsUserPositionManagerWithSig": "76453", "setUsingAsCollateralWithSig": "85107", "supplyWithSig": "153248", "updateUserDynamicConfigWithSig": "62662", "updateUserRiskPremiumWithSig": "61472", - "withdrawWithSig": "131784" + "withdrawWithSig": "131766" } \ No newline at end of file diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index 0a611b4ee..cd01f0fd1 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -186,24 +186,11 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, Multicall, IntentCo /// @inheritdoc ISignatureGateway function setSelfAsUserPositionManagerWithSig( address spoke, - address user, - bool approve, - uint256 nonce, - uint256 deadline, + EIP712Types.SetUserPositionManager calldata params, bytes calldata signature ) external onlyRegisteredSpoke(spoke) { - try - ISpoke(spoke).setUserPositionManagerWithSig( - EIP712Types.SetUserPositionManager({ - positionManager: address(this), - user: user, - approve: approve, - nonce: nonce, - deadline: deadline - }), - signature - ) - {} catch {} + require(params.positionManager == address(this), PositionManagerNotSelf()); + try ISpoke(spoke).setUserPositionManagerWithSig(params, signature) {} catch {} } /// @inheritdoc ISignatureGateway diff --git a/src/position-manager/interfaces/ISignatureGateway.sol b/src/position-manager/interfaces/ISignatureGateway.sol index 12466d732..bfff8d0e8 100644 --- a/src/position-manager/interfaces/ISignatureGateway.sol +++ b/src/position-manager/interfaces/ISignatureGateway.sol @@ -11,6 +11,9 @@ import {IGatewayBase} from 'src/position-manager/interfaces/IGatewayBase.sol'; /// @author Aave Labs /// @notice Minimal interface for protocol actions involving signed intents. interface ISignatureGateway is IMulticall, IIntentConsumer, IGatewayBase { + /// @notice Emitted when the positionManager parameter of an intent is not this contract. + error PositionManagerNotSelf(); + /// @notice Facilitates `supply` action on the specified registered `spoke` with a typed signature from `onBehalfOf`. /// @dev Supplied assets are pulled from `onBehalfOf`, prior approval to this gateway is required. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. @@ -93,17 +96,11 @@ interface ISignatureGateway is IMulticall, IIntentConsumer, IGatewayBase { /// @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 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 params The structured setUserPositionManager parameters. /// @param signature The signed bytes for the intent. function setSelfAsUserPositionManagerWithSig( address spoke, - address user, - bool approve, - uint256 nonce, - uint256 deadline, + EIP712Types.SetUserPositionManager calldata params, bytes calldata signature ) external; diff --git a/tests/gas/Gateways.Operations.gas.t.sol b/tests/gas/Gateways.Operations.gas.t.sol index 148ba56c4..2758eed0b 100644 --- a/tests/gas/Gateways.Operations.gas.t.sol +++ b/tests/gas/Gateways.Operations.gas.t.sol @@ -250,10 +250,7 @@ contract SignatureGateway_Gas_Tests is SignatureGatewayBaseTest { gateway.setSelfAsUserPositionManagerWithSig({ spoke: address(spoke1), - user: p.user, - approve: p.approve, - nonce: p.nonce, - deadline: p.deadline, + params: p, signature: signature }); vm.snapshotGasLastCall(NAMESPACE, 'setSelfAsUserPositionManagerWithSig'); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol index e4785e5cd..ed051b5a7 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol @@ -6,15 +6,51 @@ import 'tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol'; contract SignatureGatewaySetSelfAsUserPositionManagerTest is SignatureGatewayBaseTest { function test_setSelfAsUserPositionManagerWithSig_revertsWith_SpokeNotRegistered() public { + address user = vm.randomAddress(); + bool approve = vm.randomBool(); + uint256 nonce = vm.randomUint(); + uint256 deadline = vm.randomUint(); + bytes memory signature = vm.randomBytes(72); + 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) + params: EIP712Types.SetUserPositionManager({ + positionManager: address(gateway), + user: user, + approve: approve, + nonce: nonce, + deadline: deadline + }), + signature: signature + }); + } + + function test_setSelfAsUserPositionManagerWithSig_revertsWith_PositionManagerNotSelf() public { + address user = vm.randomAddress(); + bool approve = vm.randomBool(); + uint256 nonce = vm.randomUint(); + uint256 deadline = vm.randomUint(); + bytes memory signature = vm.randomBytes(72); + + address invalidPositionManager = vm.randomAddress(); + while (invalidPositionManager == address(gateway)) { + invalidPositionManager = vm.randomAddress(); + } + + vm.expectRevert(ISignatureGateway.PositionManagerNotSelf.selector); + vm.prank(vm.randomAddress()); + gateway.setSelfAsUserPositionManagerWithSig({ + spoke: address(spoke1), + params: EIP712Types.SetUserPositionManager({ + positionManager: invalidPositionManager, + user: user, + approve: approve, + nonce: nonce, + deadline: deadline + }), + signature: signature }); } @@ -39,15 +75,24 @@ contract SignatureGatewaySetSelfAsUserPositionManagerTest is SignatureGatewayBas vm.prank(vm.randomAddress()); gateway.setSelfAsUserPositionManagerWithSig({ spoke: address(spoke1), - user: user, - approve: approve, - nonce: nonce, - deadline: deadline, + params: EIP712Types.SetUserPositionManager({ + positionManager: address(gateway), + user: user, + approve: approve, + nonce: nonce, + deadline: deadline + }), signature: signature }); } function test_setSelfAsUserPositionManagerWithSig_ignores_underlying_spoke_reverts() public { + address user = vm.randomAddress(); + bool approve = vm.randomBool(); + uint256 nonce = vm.randomUint(); + uint256 deadline = vm.randomUint(); + bytes memory signature = vm.randomBytes(72); + vm.mockCallRevert( address(spoke1), ISpoke.setUserPositionManagerWithSig.selector, @@ -57,11 +102,14 @@ contract SignatureGatewaySetSelfAsUserPositionManagerTest is SignatureGatewayBas 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) + params: EIP712Types.SetUserPositionManager({ + positionManager: address(gateway), + user: user, + approve: approve, + nonce: nonce, + deadline: deadline + }), + signature: signature }); assertFalse(spoke1.isPositionManager(alice, address(gateway))); @@ -87,10 +135,7 @@ contract SignatureGatewaySetSelfAsUserPositionManagerTest is SignatureGatewayBas gateway.setSelfAsUserPositionManagerWithSig({ spoke: address(spoke1), - user: p.user, - approve: p.approve, - nonce: p.nonce, - deadline: p.deadline, + params: p, signature: signature }); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol index e12b26e0a..8c4132c11 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol @@ -247,10 +247,7 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { vm.prank(vm.randomAddress()); gateway.setSelfAsUserPositionManagerWithSig({ spoke: address(spoke1), - user: p.user, - approve: p.approve, - nonce: p.nonce, - deadline: p.deadline, + params: p, signature: signature });