From 1542a95567a56737793acf9733080d55733ede68 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:28:57 +0530 Subject: [PATCH 1/7] feat: multi posm intent --- snapshots/NativeTokenGateway.Operations.json | 4 +- snapshots/SignatureGateway.Operations.json | 16 +-- snapshots/Spoke.Getters.json | 10 +- .../Spoke.Operations.ZeroRiskPremium.json | 28 ++--- snapshots/Spoke.Operations.json | 28 ++--- src/libraries/types/EIP712Types.sol | 8 +- src/position-manager/SignatureGateway.sol | 17 +-- src/position-manager/libraries/EIP712Hash.sol | 33 ++++++ src/spoke/Spoke.sol | 42 +++---- src/spoke/interfaces/ISpoke.sol | 24 ++-- tests/Constants.sol | 4 +- tests/gas/Gateways.Operations.gas.t.sol | 5 +- tests/gas/Spoke.Operations.gas.t.sol | 34 ++---- tests/mocks/JsonBindings.sol | 4 +- .../Spoke.SetUserPositionManagerWithSig.t.sol | 111 ++++++------------ .../SignatureGateway/SignatureGateway.t.sol | 8 +- 16 files changed, 180 insertions(+), 196 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 96eb0ef3e..dc2421d8d 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { - "borrowWithSig": "215605", - "repayWithSig": "188872", - "setSelfAsUserPositionManagerWithSig": "75402", - "setUsingAsCollateralWithSig": "85053", - "supplyWithSig": "153205", - "updateUserDynamicConfigWithSig": "62769", - "updateUserRiskPremiumWithSig": "61579", - "withdrawWithSig": "131696" + "borrowWithSig": "215553", + "repayWithSig": "188907", + "setSelfAsUserPositionManagerWithSig": "85286", + "setUsingAsCollateralWithSig": "85111", + "supplyWithSig": "153181", + "updateUserDynamicConfigWithSig": "62805", + "updateUserRiskPremiumWithSig": "61615", + "withdrawWithSig": "131689" } \ 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..d6a41f1a2 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": "53458", + "setUserPositionManagerWithSig: enable": "77487", + "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..f6f39e9a0 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": "53458", + "setUserPositionManagerWithSig: enable": "77487", + "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/libraries/types/EIP712Types.sol b/src/libraries/types/EIP712Types.sol index e647206a0..fa137f151 100644 --- a/src/libraries/types/EIP712Types.sol +++ b/src/libraries/types/EIP712Types.sol @@ -7,13 +7,17 @@ pragma solidity ^0.8.20; /// @notice Defines type structs used in EIP712-typed signatures. library EIP712Types { struct SetUserPositionManager { - address positionManager; address user; - bool approve; + PositionManagerUpdate[] updates; uint256 nonce; uint256 deadline; } + struct PositionManagerUpdate { + address positionManager; + bool approve; + } + struct Permit { address owner; address spender; diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index de2194201..2d5ec7687 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -170,19 +170,14 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic /// @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 {} + // todo require one? - will be fixed with explicit args + for (uint256 i = 0; i < params.updates.length; ++i) { + params.updates[i].positionManager = address(this); + } + 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..3fab3eb54 100644 --- a/src/position-manager/libraries/EIP712Hash.sol +++ b/src/position-manager/libraries/EIP712Hash.sol @@ -36,6 +36,14 @@ library EIP712Hash { // keccak256('UpdateUserDynamicConfig(address spoke,address user,uint256 nonce,uint256 deadline)') 0xba177b1f5b5e1e709f62c19f03c97988c57752ba561de58f383ebee4e8d0a71c; + bytes32 public constant SET_USER_POSITION_MANAGER_TYPEHASH = + // keccak256('SetUserPositionManager(address user,PositionManagerUpdate[] updates,uint256 nonce,uint256 deadline)PositionManagerUpdate(address positionManager,bool approve)') + 0x585e1e37b666d270ee2f5249e16d075b3790ba51e019b5c949396d40af4cb092; + + bytes32 public constant POSITION_MANAGER_UPDATE = + // keccak256('PositionManagerUpdate(address positionManager,bool approve)') + 0x187dbd227227274b90655fb4011fc21dd749e8966fc040bd91e0b92609202565; + function hash(EIP712Types.Supply calldata params) internal pure returns (bytes32) { return keccak256( @@ -138,4 +146,29 @@ library EIP712Hash { ) ); } + + function hash( + EIP712Types.SetUserPositionManager calldata params + ) external pure returns (bytes32) { + bytes32[] memory updatesHashes = new bytes32[](params.updates.length); + for (uint256 i = 0; i < updatesHashes.length; ++i) { + updatesHashes[i] = keccak256( + abi.encode( + POSITION_MANAGER_UPDATE, + params.updates[i].positionManager, + params.updates[i].approve + ) + ); + } + return + keccak256( + abi.encode( + SET_USER_POSITION_MANAGER_TYPEHASH, + params.user, + keccak256(abi.encodePacked(updatesHashes)), + params.nonce, + params.deadline + ) + ); + } } diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index d71faaa03..4b5037830 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -8,6 +8,7 @@ import {IERC20Permit} from 'src/dependencies/openzeppelin/IERC20Permit.sol'; import {SignatureChecker} from 'src/dependencies/openzeppelin/SignatureChecker.sol'; import {AccessManagedUpgradeable} from 'src/dependencies/openzeppelin-upgradeable/AccessManagedUpgradeable.sol'; import {EIP712} from 'src/dependencies/solady/EIP712.sol'; +import {EIP712Hash, EIP712Types} from 'src/position-manager/libraries/EIP712Hash.sol'; import {MathUtils} from 'src/libraries/math/MathUtils.sol'; import {PercentageMath} from 'src/libraries/math/PercentageMath.sol'; import {WadRayMath} from 'src/libraries/math/WadRayMath.sol'; @@ -37,11 +38,12 @@ 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; + // keccak256('SetUserPositionManager(address user,PositionManagerUpdate[] updates,uint256 nonce,uint256 deadline)PositionManagerUpdate(address positionManager,bool approve)') + 0x585e1e37b666d270ee2f5249e16d075b3790ba51e019b5c949396d40af4cb092; /// @inheritdoc ISpoke address public immutable ORACLE; @@ -448,29 +450,23 @@ 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 - ) - ) + bytes32 digest = _hashTypedData(params.hash()); + require( + block.timestamp <= params.deadline && + SignatureChecker.isValidSignatureNow(params.user, digest, signature), + InvalidSignature() ); - require(SignatureChecker.isValidSignatureNow(user, digest, signature), InvalidSignature()); - _useCheckedNonce(user, nonce); - _setUserPositionManager({positionManager: positionManager, user: user, approve: approve}); + _useCheckedNonce(params.user, params.nonce); + for (uint256 i = 0; i < params.updates.length; ++i) { + _setUserPositionManager({ + positionManager: params.updates[i].positionManager, + user: params.user, + approve: params.updates[i].approve + }); + } } /// @inheritdoc ISpoke @@ -693,7 +689,7 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea function _setUserPositionManager(address positionManager, address user, bool approve) internal { PositionManagerConfig storage config = _positionManager[positionManager]; // only allow approval when position manager is active for improved UX - require(!approve || config.active, InactivePositionManager()); + require(!approve || config.active, InactivePositionManager()); // todo rm this ux check given sig batching? config.approval[user] = approve; emit SetUserPositionManager(user, positionManager, approve); } diff --git a/src/spoke/interfaces/ISpoke.sol b/src/spoke/interfaces/ISpoke.sol index 29a0a5819..664e40e7e 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; @@ -401,20 +402,17 @@ interface ISpoke is ISpokeBase, IMulticall, INoncesKeyed, IAccessManaged { /// @param approve True to approve the position manager, false to revoke approval. function setUserPositionManager(address positionManager, bool approve) external; + // / @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. + // / @param positionManagers The array of of addresses of the position managers to approve. + // / @param user The address of the user on whose behalf position manager can act. + // / @param approves The array of booleans to approve or renounce approval for `positionManagers`. + // / @param nonce The key-prefixed nonce for the signature. + // / @param deadline The deadline for the signature. + // / @param signature The EIP712-compliant signature bytes. + /// @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 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; @@ -530,7 +528,7 @@ interface ISpoke is ISpokeBase, IMulticall, INoncesKeyed, IAccessManaged { /// @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); + function SET_USER_POSITION_MANAGER_TYPEHASH() external pure returns (bytes32); /// @notice Returns the address of the AaveOracle contract. function ORACLE() external view returns (address); diff --git a/tests/Constants.sol b/tests/Constants.sol index b32ed1d4f..405e63232 100644 --- a/tests/Constants.sol +++ b/tests/Constants.sol @@ -16,7 +16,7 @@ library Constants { uint24 public constant MAX_ALLOWED_COLLATERAL_RISK = 1000_00; // 1000.00% uint256 public constant MAX_ALLOWED_DYNAMIC_CONFIG_KEY = type(uint24).max; bytes32 public constant SET_USER_POSITION_MANAGER_TYPEHASH = - // keccak256('SetUserPositionManager(address positionManager,address user,bool approve,uint256 nonce,uint256 deadline)') - 0x758d23a3c07218b7ea0b4f7f63903c4e9d5cbde72d3bcfe3e9896639025a0214; + // keccak256('SetUserPositionManager(address user,PositionManagerUpdate[] updates,uint256 nonce,uint256 deadline)PositionManagerUpdate(address positionManager,bool approve)') + 0x585e1e37b666d270ee2f5249e16d075b3790ba51e019b5c949396d40af4cb092; uint256 public constant MAX_ALLOWED_ASSET_ID = type(uint16).max; } diff --git a/tests/gas/Gateways.Operations.gas.t.sol b/tests/gas/Gateways.Operations.gas.t.sol index c2ab0743f..7f316ac71 100644 --- a/tests/gas/Gateways.Operations.gas.t.sol +++ b/tests/gas/Gateways.Operations.gas.t.sol @@ -236,10 +236,11 @@ contract SignatureGateway_Gas_Tests is SignatureGatewayBaseTest { function test_setSelfAsUserPositionManagerWithSig() public { vm.prank(alice); spoke1.useNonce(nonceKey); + EIP712Types.PositionManagerUpdate[] memory updates = new EIP712Types.PositionManagerUpdate[](1); + updates[0] = EIP712Types.PositionManagerUpdate(address(gateway), true); EIP712Types.SetUserPositionManager memory p = EIP712Types.SetUserPositionManager({ - positionManager: address(gateway), user: alice, - approve: true, + updates: updates, nonce: spoke1.nonces(alice, nonceKey), // note: this typed sig is forwarded to spoke deadline: _warpBeforeRandomDeadline() }); diff --git a/tests/gas/Spoke.Operations.gas.t.sol b/tests/gas/Spoke.Operations.gas.t.sol index aa6d2b8d9..c850c249c 100644 --- a/tests/gas/Spoke.Operations.gas.t.sol +++ b/tests/gas/Spoke.Operations.gas.t.sol @@ -299,39 +299,27 @@ contract SpokeOperations_Gas_Tests is SpokeBase { vm.prank(user); spoke.useNonce(nonceKey); - EIP712Types.SetUserPositionManager memory params = EIP712Types.SetUserPositionManager({ - positionManager: positionManager, + EIP712Types.PositionManagerUpdate[] memory updates = new EIP712Types.PositionManagerUpdate[](1); + updates[0] = EIP712Types.PositionManagerUpdate(positionManager, true); + + EIP712Types.SetUserPositionManager memory p = EIP712Types.SetUserPositionManager({ user: user, - approve: true, + updates: updates, nonce: spoke.nonces(user, nonceKey), deadline: vm.randomUint(vm.getBlockTimestamp(), MAX_SKIP_TIME) }); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPk, _getTypedDataHash(spoke, params)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPk, _getTypedDataHash(spoke, p)); bytes memory signature = abi.encodePacked(r, s, v); - spoke.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke.setUserPositionManagerWithSig(p, signature); vm.snapshotGasLastCall(NAMESPACE, 'setUserPositionManagerWithSig: enable'); - params.approve = false; - params.nonce = spoke.nonces(user, nonceKey); - (v, r, s) = vm.sign(userPk, _getTypedDataHash(spoke, params)); + p.updates[0].approve = false; + p.nonce = spoke.nonces(user, nonceKey); + (v, r, s) = vm.sign(userPk, _getTypedDataHash(spoke, p)); signature = abi.encodePacked(r, s, v); - spoke.setUserPositionManagerWithSig( - params.positionManager, - params.user, - params.approve, - params.nonce, - params.deadline, - signature - ); + spoke.setUserPositionManagerWithSig(p, signature); vm.snapshotGasLastCall(NAMESPACE, 'setUserPositionManagerWithSig: disable'); } diff --git a/tests/mocks/JsonBindings.sol b/tests/mocks/JsonBindings.sol index fad61c114..b79932edf 100644 --- a/tests/mocks/JsonBindings.sol +++ b/tests/mocks/JsonBindings.sol @@ -39,7 +39,9 @@ library JsonBindings { Vm constant vm = Vm(address(uint160(uint256(keccak256('hevm cheat code'))))); // prettier-ignore - string constant schema_SetUserPositionManager = "SetUserPositionManager(address positionManager,address user,bool approve,uint256 nonce,uint256 deadline)"; + string constant schema_SetUserPositionManager = "SetUserPositionManager(address user,PositionManagerUpdate[] updates,uint256 nonce,uint256 deadline)PositionManagerUpdate(address positionManager,bool approve)"; + // prettier-ignore + string constant schema_PositionManagerUpdate = "PositionManagerUpdate(address positionManager,bool approve)"; // prettier-ignore string constant schema_Permit = "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"; // prettier-ignore diff --git a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol index 3ff896f34..c946966ab 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') @@ -74,7 +78,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { assertEq( Constants.SET_USER_POSITION_MANAGER_TYPEHASH, keccak256( - 'SetUserPositionManager(address positionManager,address user,bool approve,uint256 nonce,uint256 deadline)' + 'SetUserPositionManager(address user,PositionManagerUpdate[] updates,uint256 nonce,uint256 deadline)PositionManagerUpdate(address positionManager,bool approve)' ) ); } @@ -93,14 +97,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { vm.expectRevert(ISpoke.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() @@ -117,14 +114,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { vm.expectRevert(ISpoke.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 +138,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 { @@ -170,20 +153,20 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { bytes memory signature = abi.encodePacked(r, s, v); vm.expectEmit(address(spoke1)); - emit ISpoke.SetUserPositionManager(params.user, params.positionManager, params.approve); - - vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, + emit ISpoke.SetUserPositionManager( params.user, - params.approve, - params.nonce, - params.deadline, - signature + params.updates[0].positionManager, + params.updates[0].approve ); + vm.prank(vm.randomAddress()); + spoke1.setUserPositionManagerWithSig(params, signature); + _assertNonceIncrement(spoke1, params.user, params.nonce); - assertEq(spoke1.isPositionManager(params.user, params.positionManager), params.approve); + assertEq( + spoke1.isPositionManager(params.user, params.updates[0].positionManager), + params.updates[0].approve + ); } function test_setUserPositionManagerWithSig_ERC1271_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() @@ -205,14 +188,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { vm.expectRevert(ISpoke.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() @@ -235,7 +211,7 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { address(smartWallet), deadline ); - invalidParams.positionManager = maliciousManager; + invalidParams.updates[0].positionManager = maliciousManager; (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, _getTypedDataHash(spoke1, invalidParams)); bytes memory signature = abi.encodePacked(r, s, v); @@ -243,16 +219,11 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { vm.prank(alice); smartWallet.approveHash(digest); + invalidParams.nonce = params.nonce; + vm.expectRevert(ISpoke.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 +257,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 { @@ -319,30 +283,31 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { bytes memory signature = abi.encodePacked(r, s, v); vm.expectEmit(address(spoke1)); - emit ISpoke.SetUserPositionManager(params.user, params.positionManager, params.approve); - - vm.prank(vm.randomAddress()); - spoke1.setUserPositionManagerWithSig( - params.positionManager, + emit ISpoke.SetUserPositionManager( params.user, - params.approve, - params.nonce, - params.deadline, - signature + params.updates[0].positionManager, + params.updates[0].approve ); + vm.prank(vm.randomAddress()); + spoke1.setUserPositionManagerWithSig(params, signature); + _assertNonceIncrement(spoke1, params.user, params.nonce); - assertEq(spoke1.isPositionManager(params.user, params.positionManager), params.approve); + assertEq( + spoke1.isPositionManager(params.user, params.updates[0].positionManager), + params.updates[0].approve + ); } function _setUserPositionManagerData( address user, uint256 deadline ) internal returns (EIP712Types.SetUserPositionManager memory) { + EIP712Types.PositionManagerUpdate[] memory updates = new EIP712Types.PositionManagerUpdate[](1); + updates[0] = EIP712Types.PositionManagerUpdate(POSITION_MANAGER, true); EIP712Types.SetUserPositionManager memory params = EIP712Types.SetUserPositionManager({ - positionManager: POSITION_MANAGER, user: user, - approve: vm.randomBool(), + updates: updates, nonce: spoke1.nonces(user, _randomNonceKey()), deadline: deadline }); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol index 577b599d2..5563aa506 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol @@ -232,17 +232,19 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { } function test_setSelfAsUserPositionManagerWithSig() public { + EIP712Types.PositionManagerUpdate[] memory updates = new EIP712Types.PositionManagerUpdate[](1); + updates[0] = EIP712Types.PositionManagerUpdate(address(gateway), true); + EIP712Types.SetUserPositionManager memory p = EIP712Types.SetUserPositionManager({ - positionManager: address(gateway), + updates: updates, user: alice, - approve: true, nonce: spoke1.nonces(address(alice), _randomNonceKey()), // note: this typed sig is forwarded to spoke deadline: _warpBeforeRandomDeadline() }); bytes memory signature = _sign(alicePk, _getTypedDataHash(spoke1, p)); vm.expectEmit(address(spoke1)); - emit ISpoke.SetUserPositionManager(alice, address(gateway), p.approve); + emit ISpoke.SetUserPositionManager(alice, address(gateway), p.updates[0].approve); vm.prank(vm.randomAddress()); gateway.setSelfAsUserPositionManagerWithSig(address(spoke1), p, signature); From 4c4efa38ce2d80a05310c7884e842a44b9b7c030 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:31:22 +0530 Subject: [PATCH 2/7] feat: skip codesize check temp --- .github/workflows/tests-pr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests-pr.yml b/.github/workflows/tests-pr.yml index cb1ff90f1..652b1307a 100644 --- a/.github/workflows/tests-pr.yml +++ b/.github/workflows/tests-pr.yml @@ -23,6 +23,7 @@ jobs: - name: Run Forge size uses: bgd-labs/github-workflows/.github/actions/foundry-size@main + continue-on-error: true - name: Run Gas report uses: bgd-labs/github-workflows/.github/actions/foundry-gas-report@main From 01b6e2e12e223f6f593903aa156954e1334b0b1d Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:42:15 +0530 Subject: [PATCH 3/7] chore: cleanup --- src/position-manager/libraries/EIP712Hash.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/position-manager/libraries/EIP712Hash.sol b/src/position-manager/libraries/EIP712Hash.sol index 3fab3eb54..c937f4002 100644 --- a/src/position-manager/libraries/EIP712Hash.sol +++ b/src/position-manager/libraries/EIP712Hash.sol @@ -8,6 +8,8 @@ import {EIP712Types} from 'src/libraries/types/EIP712Types.sol'; /// @author Aave Labs /// @notice Helper methods to hash EIP712 typed data structs. library EIP712Hash { + using EIP712Hash for *; + bytes32 public constant SUPPLY_TYPEHASH = // keccak256('Supply(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)') 0xe85497eb293c001e8483fe105efadd1d50aa0dadfc0570b27058031dfceab2e6; @@ -149,16 +151,10 @@ library EIP712Hash { function hash( EIP712Types.SetUserPositionManager calldata params - ) external pure returns (bytes32) { + ) internal pure returns (bytes32) { bytes32[] memory updatesHashes = new bytes32[](params.updates.length); for (uint256 i = 0; i < updatesHashes.length; ++i) { - updatesHashes[i] = keccak256( - abi.encode( - POSITION_MANAGER_UPDATE, - params.updates[i].positionManager, - params.updates[i].approve - ) - ); + updatesHashes[i] = params.updates[i].hash(); } return keccak256( @@ -171,4 +167,8 @@ library EIP712Hash { ) ); } + + function hash(EIP712Types.PositionManagerUpdate calldata params) internal pure returns (bytes32) { + return keccak256(abi.encode(POSITION_MANAGER_UPDATE, params.positionManager, params.approve)); + } } From c58e72ff427c6dfeb24059e235fe1fabff7e0975 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:45:53 +0530 Subject: [PATCH 4/7] chore: cleanup --- src/position-manager/SignatureGateway.sol | 2 +- src/spoke/interfaces/ISpoke.sol | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index 2d5ec7687..3d28b782a 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -173,7 +173,7 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, NoncesKeyed, Multic EIP712Types.SetUserPositionManager memory params, bytes calldata signature ) external onlyRegisteredSpoke(spoke) { - // todo require one? - will be fixed with explicit args + // todo require one? regardless, will be removed with explicit args for (uint256 i = 0; i < params.updates.length; ++i) { params.updates[i].positionManager = address(this); } diff --git a/src/spoke/interfaces/ISpoke.sol b/src/spoke/interfaces/ISpoke.sol index 664e40e7e..23b5ed628 100644 --- a/src/spoke/interfaces/ISpoke.sol +++ b/src/spoke/interfaces/ISpoke.sol @@ -402,15 +402,10 @@ interface ISpoke is ISpokeBase, IMulticall, INoncesKeyed, IAccessManaged { /// @param approve True to approve the position manager, false to revoke approval. function setUserPositionManager(address positionManager, bool approve) external; - // / @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. - // / @param positionManagers The array of of addresses of the position managers to approve. - // / @param user The address of the user on whose behalf position manager can act. - // / @param approves The array of booleans to approve or renounce approval for `positionManagers`. - // / @param nonce The key-prefixed nonce for the signature. - // / @param deadline The deadline for the signature. - // / @param signature The EIP712-compliant signature bytes. - /// @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 params The structured setUserPositionManager parameters. + /// @param signature The EIP712-compliant signature bytes. function setUserPositionManagerWithSig( EIP712Types.SetUserPositionManager calldata params, bytes calldata signature From cb01b58ff042dbdee858fd5e9c17a1df4e6d7af9 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 16 Dec 2025 07:45:37 +0530 Subject: [PATCH 5/7] chore: snapshot --- snapshots/SignatureGateway.Operations.json | 2 +- snapshots/Spoke.Operations.ZeroRiskPremium.json | 4 ++-- snapshots/Spoke.Operations.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index dc2421d8d..f05012d18 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,7 +1,7 @@ { "borrowWithSig": "215553", "repayWithSig": "188907", - "setSelfAsUserPositionManagerWithSig": "85286", + "setSelfAsUserPositionManagerWithSig": "80492", "setUsingAsCollateralWithSig": "85111", "supplyWithSig": "153181", "updateUserDynamicConfigWithSig": "62805", diff --git a/snapshots/Spoke.Operations.ZeroRiskPremium.json b/snapshots/Spoke.Operations.ZeroRiskPremium.json index d6a41f1a2..03006c0dd 100644 --- a/snapshots/Spoke.Operations.ZeroRiskPremium.json +++ b/snapshots/Spoke.Operations.ZeroRiskPremium.json @@ -10,8 +10,8 @@ "permitReserve + supply + enable collateral (multicall)": "160551", "repay: full": "126159", "repay: partial": "131048", - "setUserPositionManagerWithSig: disable": "53458", - "setUserPositionManagerWithSig: enable": "77487", + "setUserPositionManagerWithSig: disable": "48664", + "setUserPositionManagerWithSig: enable": "72693", "supply + enable collateral (multicall)": "140602", "supply: 0 borrows, collateral disabled": "123679", "supply: 0 borrows, collateral enabled": "106601", diff --git a/snapshots/Spoke.Operations.json b/snapshots/Spoke.Operations.json index f6f39e9a0..53bf61a64 100644 --- a/snapshots/Spoke.Operations.json +++ b/snapshots/Spoke.Operations.json @@ -10,8 +10,8 @@ "permitReserve + supply + enable collateral (multicall)": "160551", "repay: full": "120321", "repay: partial": "139610", - "setUserPositionManagerWithSig: disable": "53458", - "setUserPositionManagerWithSig: enable": "77487", + "setUserPositionManagerWithSig: disable": "48664", + "setUserPositionManagerWithSig: enable": "72693", "supply + enable collateral (multicall)": "140602", "supply: 0 borrows, collateral disabled": "123679", "supply: 0 borrows, collateral enabled": "106601", From 7e8eab507eda304086067259aa9230995070a87c Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 16 Dec 2025 08:30:02 +0530 Subject: [PATCH 6/7] feat: bring codesize under control --- .github/workflows/tests-pr.yml | 1 - snapshots/NativeTokenGateway.Operations.json | 12 ++-- snapshots/SignatureGateway.Operations.json | 16 ++--- snapshots/Spoke.Getters.json | 10 ++-- .../Spoke.Operations.ZeroRiskPremium.json | 58 +++++++++---------- snapshots/Spoke.Operations.json | 58 +++++++++---------- src/libraries/misc/SignatureChecker.sol | 23 ++++++++ src/spoke/Spoke.sol | 7 +-- src/spoke/interfaces/ISpoke.sol | 4 -- tests/Base.t.sol | 2 +- .../Spoke.SetUserPositionManagerWithSig.t.sol | 16 ++++- 11 files changed, 116 insertions(+), 91 deletions(-) create mode 100644 src/libraries/misc/SignatureChecker.sol diff --git a/.github/workflows/tests-pr.yml b/.github/workflows/tests-pr.yml index 652b1307a..cb1ff90f1 100644 --- a/.github/workflows/tests-pr.yml +++ b/.github/workflows/tests-pr.yml @@ -23,7 +23,6 @@ jobs: - name: Run Forge size uses: bgd-labs/github-workflows/.github/actions/foundry-size@main - continue-on-error: true - name: Run Gas report uses: bgd-labs/github-workflows/.github/actions/foundry-gas-report@main 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 f05012d18..c79664408 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,10 +1,10 @@ { - "borrowWithSig": "215553", - "repayWithSig": "188907", - "setSelfAsUserPositionManagerWithSig": "80492", - "setUsingAsCollateralWithSig": "85111", - "supplyWithSig": "153181", - "updateUserDynamicConfigWithSig": "62805", - "updateUserRiskPremiumWithSig": "61615", - "withdrawWithSig": "131689" + "borrowWithSig": "215531", + "repayWithSig": "188841", + "setSelfAsUserPositionManagerWithSig": "84161", + "setUsingAsCollateralWithSig": "85089", + "supplyWithSig": "153146", + "updateUserDynamicConfigWithSig": "62893", + "updateUserRiskPremiumWithSig": "61593", + "withdrawWithSig": "131654" } \ 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 03006c0dd..ad7dbf00a 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", - "setUserPositionManagerWithSig: disable": "48664", - "setUserPositionManagerWithSig: enable": "72693", - "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" + "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": "52333", + "setUserPositionManagerWithSig: enable": "76362", + "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 53bf61a64..26bd34ec0 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", - "setUserPositionManagerWithSig: disable": "48664", - "setUserPositionManagerWithSig: enable": "72693", - "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" + "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": "52333", + "setUserPositionManagerWithSig: enable": "76362", + "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 diff --git a/src/libraries/misc/SignatureChecker.sol b/src/libraries/misc/SignatureChecker.sol new file mode 100644 index 000000000..df64abea8 --- /dev/null +++ b/src/libraries/misc/SignatureChecker.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: UNLICENSED +// Copyright (c) 2025 Aave Labs +pragma solidity ^0.8.0; + +import {SignatureChecker as OpenZeppelinSignatureChecker} from 'src/dependencies/openzeppelin/SignatureChecker.sol'; + +/// @title SignatureChecker +/// @author Aave Labs +library SignatureChecker { + /// @notice Checks if a signature is valid for a given signer and data hash. + /// @dev External wrapper around OpenZeppelin's SignatureChecker.isValidSignatureNow to reduce code size at the expense of an external delegatecall. + /// @param signer The address of the signer. + /// @param hash The hash of the data to be signed. + /// @param signature The signature bytes. + /// @return True if the signature is valid, false otherwise. + function isValidSignatureNow( + address signer, + bytes32 hash, + bytes memory signature + ) external view returns (bool) { + return OpenZeppelinSignatureChecker.isValidSignatureNow(signer, hash, signature); + } +} diff --git a/src/spoke/Spoke.sol b/src/spoke/Spoke.sol index 4b5037830..6281b909c 100644 --- a/src/spoke/Spoke.sol +++ b/src/spoke/Spoke.sol @@ -5,7 +5,7 @@ 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 {SignatureChecker} from 'src/libraries/misc/SignatureChecker.sol'; import {AccessManagedUpgradeable} from 'src/dependencies/openzeppelin-upgradeable/AccessManagedUpgradeable.sol'; import {EIP712} from 'src/dependencies/solady/EIP712.sol'; import {EIP712Hash, EIP712Types} from 'src/position-manager/libraries/EIP712Hash.sol'; @@ -40,11 +40,6 @@ abstract contract Spoke is ISpoke, Multicall, NoncesKeyed, AccessManagedUpgradea using UserPositionDebt for ISpoke.UserPosition; using EIP712Hash for EIP712Types.SetUserPositionManager; - /// @inheritdoc ISpoke - bytes32 public constant SET_USER_POSITION_MANAGER_TYPEHASH = - // keccak256('SetUserPositionManager(address user,PositionManagerUpdate[] updates,uint256 nonce,uint256 deadline)PositionManagerUpdate(address positionManager,bool approve)') - 0x585e1e37b666d270ee2f5249e16d075b3790ba51e019b5c949396d40af4cb092; - /// @inheritdoc ISpoke address public immutable ORACLE; diff --git a/src/spoke/interfaces/ISpoke.sol b/src/spoke/interfaces/ISpoke.sol index 23b5ed628..71a3e905d 100644 --- a/src/spoke/interfaces/ISpoke.sol +++ b/src/spoke/interfaces/ISpoke.sol @@ -521,10 +521,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 pure returns (bytes32); - /// @notice Returns the address of the AaveOracle contract. function ORACLE() external view returns (address); } diff --git a/tests/Base.t.sol b/tests/Base.t.sol index 424bc3683..1385131fe 100644 --- a/tests/Base.t.sol +++ b/tests/Base.t.sol @@ -33,7 +33,7 @@ import {IERC1967} from 'src/dependencies/openzeppelin/IERC1967.sol'; import {WadRayMath} from 'src/libraries/math/WadRayMath.sol'; import {MathUtils} from 'src/libraries/math/MathUtils.sol'; import {PercentageMath} from 'src/libraries/math/PercentageMath.sol'; -import {EIP712Types} from 'src/libraries/types/EIP712Types.sol'; +import {EIP712Hash, EIP712Types} from 'src/position-manager/libraries/EIP712Hash.sol'; import {Roles} from 'src/libraries/types/Roles.sol'; import {Rescuable, IRescuable} from 'src/utils/Rescuable.sol'; import {NoncesKeyed, INoncesKeyed} from 'src/utils/NoncesKeyed.sol'; diff --git a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol index c946966ab..62db02c3a 100644 --- a/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol +++ b/tests/unit/Spoke/Spoke.SetUserPositionManagerWithSig.t.sol @@ -66,10 +66,10 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { assertEq(spoke.DOMAIN_SEPARATOR(), expectedDomainSeparator); } - function test_setUserPositionManager_typeHash() public view { + function test_setUserPositionManager_typeHash() public pure { assertEq( Constants.SET_USER_POSITION_MANAGER_TYPEHASH, - spoke1.SET_USER_POSITION_MANAGER_TYPEHASH() + EIP712Hash.SET_USER_POSITION_MANAGER_TYPEHASH ); assertEq( Constants.SET_USER_POSITION_MANAGER_TYPEHASH, @@ -83,6 +83,18 @@ contract SpokeSetUserPositionManagerWithSigTest is SpokeBase { ); } + function test_positionManagerUpdate_typeHash() public pure { + assertEq( + EIP712Hash.POSITION_MANAGER_UPDATE, + keccak256('PositionManagerUpdate(address positionManager,bool approve)') + ); + assertEq(EIP712Hash.POSITION_MANAGER_UPDATE, vm.eip712HashType('PositionManagerUpdate')); + assertEq( + EIP712Hash.POSITION_MANAGER_UPDATE, + keccak256('PositionManagerUpdate(address positionManager,bool approve)') + ); + } + function test_setUserPositionManagerWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() public { From 9bc3544c25504149a7dc9cd0fd19861871b3eff7 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:00:12 +0530 Subject: [PATCH 7/7] chore: compile --- .../SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol | 2 +- tests/unit/misc/SignatureGateway/SignatureGateway.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol index df8661bf4..7c2815eb0 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.SetSelfAsUserPositionManagerWithSig.t.sol @@ -65,7 +65,7 @@ contract SignatureGatewaySetSelfAsUserPositionManagerTest is SignatureGatewayBas assertFalse(spoke1.isPositionManager(alice, address(gateway))); } - function test_setSelfAsUserPositionManagerWithSig_single_update() public { + function test_setSelfAsUserPositionManagerWithSig() public { uint192 nonceKey = _randomNonceKey(); vm.prank(alice); spoke1.useNonce(nonceKey); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol index 8c04e8ca3..6166d58d5 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol @@ -250,7 +250,7 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { gateway.setSelfAsUserPositionManagerWithSig({ spoke: address(spoke1), user: p.user, - approve: p.approve, + approve: p.updates[0].approve, nonce: p.nonce, deadline: p.deadline, signature: signature