From c02a7af06087ce7ede045eaab5040e9b575e89e3 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 20 Jan 2026 15:43:26 +0530 Subject: [PATCH 1/2] feat: naive strongly typed sig batcher --- .../SignatureGateway.BatchOperations.json | 8 + snapshots/SignatureGateway.Operations.json | 18 +- src/position-manager/SignatureGateway.sol | 169 +++++++ .../interfaces/IGatewayBase.sol | 9 + .../interfaces/ISignatureGateway.sol | 92 ++++ .../libraries/BatchEIP712.sol | 288 ++++++++++++ tests/gas/Gateways.Operations.gas.t.sol | 284 ++++++++++++ .../SignatureGateway.Batch.t.sol | 424 ++++++++++++++++++ 8 files changed, 1283 insertions(+), 9 deletions(-) create mode 100644 snapshots/SignatureGateway.BatchOperations.json create mode 100644 src/position-manager/libraries/BatchEIP712.sol create mode 100644 tests/unit/misc/SignatureGateway/SignatureGateway.Batch.t.sol diff --git a/snapshots/SignatureGateway.BatchOperations.json b/snapshots/SignatureGateway.BatchOperations.json new file mode 100644 index 000000000..ed59359fa --- /dev/null +++ b/snapshots/SignatureGateway.BatchOperations.json @@ -0,0 +1,8 @@ +{ + "executeBatchWithSig: 1 action (supply)": "157129", + "executeBatchWithSig: 2 actions (borrow+repay)": "280249", + "executeBatchWithSig: 2 actions (supply+setCollateral)": "166166", + "executeBatchWithSig: 2 actions (supply+withdraw)": "206147", + "executeBatchWithSig: 3 actions (supply+setCollateral+withdraw)": "234510", + "executeBatchWithSig: 5 actions": "436106" +} \ No newline at end of file diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index 29eb1d85c..2bcb00ac1 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,12 +1,12 @@ { - "borrowWithSig": "212847", - "repayWithPermit2": "213061", - "repayWithSig": "186400", - "setSelfAsUserPositionManagerWithSig": "75432", - "setUsingAsCollateralWithSig": "85385", - "supplyWithPermit2": "172970", - "supplyWithSig": "151925", + "borrowWithSig": "212769", + "repayWithPermit2": "213005", + "repayWithSig": "186322", + "setSelfAsUserPositionManagerWithSig": "75426", + "setUsingAsCollateralWithSig": "85373", + "supplyWithPermit2": "172908", + "supplyWithSig": "151863", "updateUserDynamicConfigWithSig": "63079", - "updateUserRiskPremiumWithSig": "62083", - "withdrawWithSig": "130601" + "updateUserRiskPremiumWithSig": "62040", + "withdrawWithSig": "130539" } \ No newline at end of file diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index f55a588a7..8f50fe41d 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -5,6 +5,7 @@ pragma solidity 0.8.28; import {SafeERC20, IERC20} from 'src/dependencies/openzeppelin/SafeERC20.sol'; import {IERC20Permit} from 'src/dependencies/openzeppelin/IERC20Permit.sol'; import {EIP712Hash} from 'src/position-manager/libraries/EIP712Hash.sol'; +import {BatchEIP712} from 'src/position-manager/libraries/BatchEIP712.sol'; import {MathUtils} from 'src/libraries/math/MathUtils.sol'; import {GatewayBase} from 'src/position-manager/GatewayBase.sol'; import {IntentConsumer} from 'src/utils/IntentConsumer.sol'; @@ -322,6 +323,174 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, IntentConsumer, Mul return ISpoke(params.spoke).repay(params.reserveId, repayAmount, params.onBehalfOf); } + /// @inheritdoc ISignatureGateway + function executeBatchWithSig( + uint8[] memory actionTypes, + bytes[] memory actionData, + address onBehalfOf, + uint256 nonce, + uint256 deadline, + bytes calldata signature + ) external { + uint256 len = actionTypes.length; + require(len == actionData.length, LengthMismatch()); + require(len > 0 && len <= 10, InvalidBatchSize()); + + bytes32 structHash = BatchEIP712.hashBatch( + actionTypes, + actionData, + onBehalfOf, + nonce, + deadline + ); + _verifyAndConsumeIntent({ + signer: onBehalfOf, + intentHash: structHash, + nonce: nonce, + deadline: deadline, + signature: signature + }); + + for (uint256 i = 0; i < len; i++) { + _executeAction(actionTypes[i], actionData[i], onBehalfOf); + } + } + + /// @dev Execute a single action from the batch. + /// @param actionType The action type enum value. + /// @param actionData The ABI-encoded action struct. + /// @param onBehalfOf The user on whose behalf the action is performed. + function _executeAction(uint8 actionType, bytes memory actionData, address onBehalfOf) internal { + if (actionType == uint8(ISignatureGateway.ActionType.Supply)) { + _executeSupplyAction(actionData, onBehalfOf); + } else if (actionType == uint8(ISignatureGateway.ActionType.Withdraw)) { + _executeWithdrawAction(actionData, onBehalfOf); + } else if (actionType == uint8(ISignatureGateway.ActionType.Borrow)) { + _executeBorrowAction(actionData, onBehalfOf); + } else if (actionType == uint8(ISignatureGateway.ActionType.Repay)) { + _executeRepayAction(actionData, onBehalfOf); + } else if (actionType == uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) { + _executeSetUsingAsCollateralAction(actionData, onBehalfOf); + } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) { + _executeUpdateUserRiskPremiumAction(actionData, onBehalfOf); + } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) { + _executeUpdateUserDynamicConfigAction(actionData, onBehalfOf); + } else { + revert InvalidActionType(); + } + } + + /// @dev Execute a supply action. + function _executeSupplyAction(bytes memory actionData, address onBehalfOf) internal { + ISignatureGateway.SupplyAction memory action = abi.decode( + actionData, + (ISignatureGateway.SupplyAction) + ); + _isSpokeValid(action.spoke); + + IERC20 underlying = IERC20(_getReserveUnderlying(action.spoke, action.reserveId)); + underlying.safeTransferFrom(onBehalfOf, address(this), action.amount); + underlying.forceApprove(action.spoke, action.amount); + + ISpoke(action.spoke).supply(action.reserveId, action.amount, onBehalfOf); + } + + /// @dev Execute a withdraw action. + function _executeWithdrawAction(bytes memory actionData, address onBehalfOf) internal { + ISignatureGateway.WithdrawAction memory action = abi.decode( + actionData, + (ISignatureGateway.WithdrawAction) + ); + _isSpokeValid(action.spoke); + + IERC20 underlying = IERC20(_getReserveUnderlying(action.spoke, action.reserveId)); + (, uint256 withdrawnAmount) = ISpoke(action.spoke).withdraw( + action.reserveId, + action.amount, + onBehalfOf + ); + underlying.safeTransfer(onBehalfOf, withdrawnAmount); + } + + /// @dev Execute a borrow action. + function _executeBorrowAction(bytes memory actionData, address onBehalfOf) internal { + ISignatureGateway.BorrowAction memory action = abi.decode( + actionData, + (ISignatureGateway.BorrowAction) + ); + _isSpokeValid(action.spoke); + + IERC20 underlying = IERC20(_getReserveUnderlying(action.spoke, action.reserveId)); + (, uint256 borrowedAmount) = ISpoke(action.spoke).borrow( + action.reserveId, + action.amount, + onBehalfOf + ); + underlying.safeTransfer(onBehalfOf, borrowedAmount); + } + + /// @dev Execute a repay action. + function _executeRepayAction(bytes memory actionData, address onBehalfOf) internal { + ISignatureGateway.RepayAction memory action = abi.decode( + actionData, + (ISignatureGateway.RepayAction) + ); + _isSpokeValid(action.spoke); + + IERC20 underlying = IERC20(_getReserveUnderlying(action.spoke, action.reserveId)); + uint256 repayAmount = MathUtils.min( + action.amount, + ISpoke(action.spoke).getUserTotalDebt(action.reserveId, onBehalfOf) + ); + + underlying.safeTransferFrom(onBehalfOf, address(this), repayAmount); + underlying.forceApprove(action.spoke, repayAmount); + + ISpoke(action.spoke).repay(action.reserveId, repayAmount, onBehalfOf); + } + + /// @dev Execute a setUsingAsCollateral action. + function _executeSetUsingAsCollateralAction( + bytes memory actionData, + address onBehalfOf + ) internal { + ISignatureGateway.SetUsingAsCollateralAction memory action = abi.decode( + actionData, + (ISignatureGateway.SetUsingAsCollateralAction) + ); + _isSpokeValid(action.spoke); + + ISpoke(action.spoke).setUsingAsCollateral(action.reserveId, action.useAsCollateral, onBehalfOf); + } + + /// @dev Execute an updateUserRiskPremium action. + function _executeUpdateUserRiskPremiumAction( + bytes memory actionData, + address onBehalfOf + ) internal { + ISignatureGateway.UpdateUserRiskPremiumAction memory action = abi.decode( + actionData, + (ISignatureGateway.UpdateUserRiskPremiumAction) + ); + _isSpokeValid(action.spoke); + + ISpoke(action.spoke).updateUserRiskPremium(onBehalfOf); + } + + /// @dev Execute an updateUserDynamicConfig action. + function _executeUpdateUserDynamicConfigAction( + bytes memory actionData, + address onBehalfOf + ) internal { + ISignatureGateway.UpdateUserDynamicConfigAction memory action = abi.decode( + actionData, + (ISignatureGateway.UpdateUserDynamicConfigAction) + ); + _isSpokeValid(action.spoke); + + ISpoke(action.spoke).updateUserDynamicConfig(onBehalfOf); + } + function _domainNameAndVersion() internal pure override returns (string memory, string memory) { return ('SignatureGateway', '1'); } diff --git a/src/position-manager/interfaces/IGatewayBase.sol b/src/position-manager/interfaces/IGatewayBase.sol index 0b96f4c14..6efffaaa1 100644 --- a/src/position-manager/interfaces/IGatewayBase.sol +++ b/src/position-manager/interfaces/IGatewayBase.sol @@ -20,6 +20,15 @@ interface IGatewayBase is IRescuable { /// @notice Thrown when the specified spoke is not registered. error SpokeNotRegistered(); + /// @notice Thrown when array lengths do not match. + error LengthMismatch(); + + /// @notice Thrown when the batch size is invalid. + error InvalidBatchSize(); + + /// @notice Thrown when the action type is invalid. + error InvalidActionType(); + /// @notice Allows contract to renounce its position manager role for `user`. /// @dev Only authorized caller to invoke this method. /// @param spoke The address of the registered `spoke`. diff --git a/src/position-manager/interfaces/ISignatureGateway.sol b/src/position-manager/interfaces/ISignatureGateway.sol index 733f93f7d..403b9de5e 100644 --- a/src/position-manager/interfaces/ISignatureGateway.sol +++ b/src/position-manager/interfaces/ISignatureGateway.sol @@ -11,6 +11,79 @@ import {ISignatureTransfer} from 'lib/permit2/src/interfaces/ISignatureTransfer. /// @author Aave Labs /// @notice Minimal interface for protocol actions involving signed intents. interface ISignatureGateway is IGatewayBase, IIntentConsumer, IMulticall { + /// @notice Action type enumeration for batch operations. + enum ActionType { + Supply, // 0 + Withdraw, // 1 + Borrow, // 2 + Repay, // 3 + SetUsingAsCollateral, // 4 + UpdateUserRiskPremium, // 5 + UpdateUserDynamicConfig // 6 + } + + /// @notice Batch action for supplying assets (without nonce/deadline/onBehalfOf - lifted to batch level). + /// @param spoke The address of the registered spoke. + /// @param reserveId The identifier of the reserve. + /// @param amount The amount of assets to supply. + struct SupplyAction { + address spoke; + uint256 reserveId; + uint256 amount; + } + + /// @notice Batch action for withdrawing assets. + /// @param spoke The address of the registered spoke. + /// @param reserveId The identifier of the reserve. + /// @param amount The amount of assets to withdraw. + struct WithdrawAction { + address spoke; + uint256 reserveId; + uint256 amount; + } + + /// @notice Batch action for borrowing assets. + /// @param spoke The address of the registered spoke. + /// @param reserveId The identifier of the reserve. + /// @param amount The amount of assets to borrow. + struct BorrowAction { + address spoke; + uint256 reserveId; + uint256 amount; + } + + /// @notice Batch action for repaying assets. + /// @param spoke The address of the registered spoke. + /// @param reserveId The identifier of the reserve. + /// @param amount The amount of assets to repay. + struct RepayAction { + address spoke; + uint256 reserveId; + uint256 amount; + } + + /// @notice Batch action for setting collateral usage. + /// @param spoke The address of the registered spoke. + /// @param reserveId The identifier of the reserve. + /// @param useAsCollateral True to enable the reserve as collateral, false to disable it. + struct SetUsingAsCollateralAction { + address spoke; + uint256 reserveId; + bool useAsCollateral; + } + + /// @notice Batch action for updating user risk premium. + /// @param spoke The address of the registered spoke. + struct UpdateUserRiskPremiumAction { + address spoke; + } + + /// @notice Batch action for updating user dynamic config. + /// @param spoke The address of the registered spoke. + struct UpdateUserDynamicConfigAction { + address spoke; + } + /// @notice Intent data to supply assets to a reserve. /// @param spoke The address of the registered spoke. /// @param reserveId The identifier of the reserve. @@ -259,6 +332,25 @@ interface ISignatureGateway is IGatewayBase, IIntentConsumer, IMulticall { bytes calldata signature ) external returns (uint256, uint256); + /// @notice Executes a batch of actions with a single signature. + /// @dev The batch type string is constructed dynamically based on the action types. + /// @dev All actions in the batch are executed for the same `onBehalfOf` address. + /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. + /// @param actionTypes Array of action types (ActionType enum values). + /// @param actionData Array of ABI-encoded action structs corresponding to each action type. + /// @param onBehalfOf The address of the user on whose behalf all actions are performed. + /// @param nonce The key-prefixed nonce for the batch signature. + /// @param deadline The deadline for the batch intent. + /// @param signature The signed bytes for the batch intent. + function executeBatchWithSig( + uint8[] memory actionTypes, + bytes[] memory actionData, + address onBehalfOf, + uint256 nonce, + uint256 deadline, + bytes calldata signature + ) external; + /// @notice Returns the type hash for the Supply intent. function SUPPLY_TYPEHASH() external view returns (bytes32); diff --git a/src/position-manager/libraries/BatchEIP712.sol b/src/position-manager/libraries/BatchEIP712.sol new file mode 100644 index 000000000..2b1036cf6 --- /dev/null +++ b/src/position-manager/libraries/BatchEIP712.sol @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: UNLICENSED +// Copyright (c) 2025 Aave Labs +pragma solidity ^0.8.20; + +import {ISignatureGateway} from 'src/position-manager/interfaces/ISignatureGateway.sol'; + +/// @title BatchEIP712 library +/// @author Aave Labs +/// @notice Helper methods to construct dynamic EIP-712 type strings and hash batch actions. +/// @dev Constructs type strings at runtime to maintain full wallet UX visibility for batch signing. +library BatchEIP712 { + string internal constant SUPPLY_ACTION_NAME = 'SupplyAction'; + string internal constant WITHDRAW_ACTION_NAME = 'WithdrawAction'; + string internal constant BORROW_ACTION_NAME = 'BorrowAction'; + string internal constant REPAY_ACTION_NAME = 'RepayAction'; + string internal constant SET_USING_AS_COLLATERAL_ACTION_NAME = 'SetUsingAsCollateralAction'; + string internal constant UPDATE_USER_RISK_PREMIUM_ACTION_NAME = 'UpdateUserRiskPremiumAction'; + string internal constant UPDATE_USER_DYNAMIC_CONFIG_ACTION_NAME = 'UpdateUserDynamicConfigAction'; + + string internal constant BORROW_ACTION_DEF = + 'BorrowAction(address spoke,uint256 reserveId,uint256 amount)'; + string internal constant REPAY_ACTION_DEF = + 'RepayAction(address spoke,uint256 reserveId,uint256 amount)'; + string internal constant SET_USING_AS_COLLATERAL_ACTION_DEF = + 'SetUsingAsCollateralAction(address spoke,uint256 reserveId,bool useAsCollateral)'; + string internal constant SUPPLY_ACTION_DEF = + 'SupplyAction(address spoke,uint256 reserveId,uint256 amount)'; + string internal constant UPDATE_USER_DYNAMIC_CONFIG_ACTION_DEF = + 'UpdateUserDynamicConfigAction(address spoke)'; + string internal constant UPDATE_USER_RISK_PREMIUM_ACTION_DEF = + 'UpdateUserRiskPremiumAction(address spoke)'; + string internal constant WITHDRAW_ACTION_DEF = + 'WithdrawAction(address spoke,uint256 reserveId,uint256 amount)'; + + bytes32 internal constant SUPPLY_ACTION_TYPEHASH = + // keccak256('SupplyAction(address spoke,uint256 reserveId,uint256 amount)') + 0x92108fb6c1c54e895857cadeb15a1d0ff251d05ab5bc45c397f7f0bf4513524f; + + bytes32 internal constant WITHDRAW_ACTION_TYPEHASH = + // keccak256('WithdrawAction(address spoke,uint256 reserveId,uint256 amount)') + 0x9886e55b7e2df773f3c842b4432b6e809e8669053f302bffdacc143660738090; + + bytes32 internal constant BORROW_ACTION_TYPEHASH = + // keccak256('BorrowAction(address spoke,uint256 reserveId,uint256 amount)') + 0x2d06ff6c841f7e36ccc14b960e3f08ce8e6eb41ab93b31281ffa7ad44e21026c; + + bytes32 internal constant REPAY_ACTION_TYPEHASH = + // keccak256('RepayAction(address spoke,uint256 reserveId,uint256 amount)') + 0xd0bfcdb753a3de34964385d0931b97edfc843abb115302d37b474e15a31220e4; + + bytes32 internal constant SET_USING_AS_COLLATERAL_ACTION_TYPEHASH = + // keccak256('SetUsingAsCollateralAction(address spoke,uint256 reserveId,bool useAsCollateral)') + 0x274cee27fcc6e1e5383183a5d15ceaba083b45ceb85fd75d497dc482b444ff89; + + bytes32 internal constant UPDATE_USER_RISK_PREMIUM_ACTION_TYPEHASH = + // keccak256('UpdateUserRiskPremiumAction(address spoke)') + 0x3d45bb6df468588f10dd8ff3cb2c53086fc7d49dc451b275b868a3c2b32e50d7; + + bytes32 internal constant UPDATE_USER_DYNAMIC_CONFIG_ACTION_TYPEHASH = + // keccak256('UpdateUserDynamicConfigAction(address spoke)') + 0x8548aa46ece028df12b6ecd855470cbc563c7599931c9c156ae140206e8853ca; + + bytes internal constant ACTION_FIELD_PREFIX = ' action'; + bytes internal constant FIELD_SEPARATOR = ','; + + bytes internal constant BATCH_PREFIX = 'Batch('; + bytes internal constant BATCH_SUFFIX = 'address onBehalfOf,uint256 nonce,uint256 deadline)'; + + /// @notice Build the batch type hash from an array of action types. + /// @dev Constructs the type string dynamically and returns its keccak256 hash. + /// @param actionTypes Array of action type enum values. + /// @return The keccak256 hash of the constructed type string. + function buildBatchTypeHash(uint8[] memory actionTypes) internal pure returns (bytes32) { + return keccak256(bytes(buildBatchTypeString(actionTypes))); + } + + /// @notice Build the full batch type string from an array of action types. + /// @dev The type string format is: + /// Batch({ActionType} action0,{ActionType} action1,...,address onBehalfOf,uint256 nonce,uint256 deadline){TypeDefs} + /// where {TypeDefs} are the nested type definitions in alphabetical order. + /// @param actionTypes Array of action type enum values. + /// @return The constructed type string. + function buildBatchTypeString(uint8[] memory actionTypes) internal pure returns (string memory) { + uint256 len = actionTypes.length; + + // Track which action types are used (bitmap for deduplication) + uint8 usedTypes = 0; + + // Build the Batch(...) part + bytes memory batchPart = BATCH_PREFIX; + + for (uint256 i = 0; i < len; i++) { + uint8 actionType = actionTypes[i]; + usedTypes |= uint8(1 << actionType); + + // Append "{ActionTypeName} action{i}," + batchPart = bytes.concat( + batchPart, + bytes(_getActionTypeName(actionType)), + ACTION_FIELD_PREFIX, + bytes(_uintToString(i)), + FIELD_SEPARATOR + ); + } + + // Append the fixed suffix + batchPart = bytes.concat(batchPart, BATCH_SUFFIX); + + // Append type definitions in alphabetical order + // Order: Borrow < Repay < SetUsingAsCollateral < Supply < UpdateUserDynamicConfig < UpdateUserRiskPremium < Withdraw + bytes memory typeDefs = ''; + + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Borrow)) != 0) { + typeDefs = bytes.concat(typeDefs, bytes(BORROW_ACTION_DEF)); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Repay)) != 0) { + typeDefs = bytes.concat(typeDefs, bytes(REPAY_ACTION_DEF)); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) != 0) { + typeDefs = bytes.concat(typeDefs, bytes(SET_USING_AS_COLLATERAL_ACTION_DEF)); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Supply)) != 0) { + typeDefs = bytes.concat(typeDefs, bytes(SUPPLY_ACTION_DEF)); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) != 0) { + typeDefs = bytes.concat(typeDefs, bytes(UPDATE_USER_DYNAMIC_CONFIG_ACTION_DEF)); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) != 0) { + typeDefs = bytes.concat(typeDefs, bytes(UPDATE_USER_RISK_PREMIUM_ACTION_DEF)); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Withdraw)) != 0) { + typeDefs = bytes.concat(typeDefs, bytes(WITHDRAW_ACTION_DEF)); + } + + return string(bytes.concat(batchPart, typeDefs)); + } + + /// @notice Hash a specific action based on its type. + /// @param actionType The action type enum value. + /// @param actionData The ABI-encoded action struct. + /// @return The struct hash of the action. + function hashAction(uint8 actionType, bytes memory actionData) internal pure returns (bytes32) { + if (actionType == uint8(ISignatureGateway.ActionType.Supply)) { + ISignatureGateway.SupplyAction memory action = abi.decode( + actionData, + (ISignatureGateway.SupplyAction) + ); + return + keccak256( + abi.encode(SUPPLY_ACTION_TYPEHASH, action.spoke, action.reserveId, action.amount) + ); + } else if (actionType == uint8(ISignatureGateway.ActionType.Withdraw)) { + ISignatureGateway.WithdrawAction memory action = abi.decode( + actionData, + (ISignatureGateway.WithdrawAction) + ); + return + keccak256( + abi.encode(WITHDRAW_ACTION_TYPEHASH, action.spoke, action.reserveId, action.amount) + ); + } else if (actionType == uint8(ISignatureGateway.ActionType.Borrow)) { + ISignatureGateway.BorrowAction memory action = abi.decode( + actionData, + (ISignatureGateway.BorrowAction) + ); + return + keccak256( + abi.encode(BORROW_ACTION_TYPEHASH, action.spoke, action.reserveId, action.amount) + ); + } else if (actionType == uint8(ISignatureGateway.ActionType.Repay)) { + ISignatureGateway.RepayAction memory action = abi.decode( + actionData, + (ISignatureGateway.RepayAction) + ); + return + keccak256(abi.encode(REPAY_ACTION_TYPEHASH, action.spoke, action.reserveId, action.amount)); + } else if (actionType == uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) { + ISignatureGateway.SetUsingAsCollateralAction memory action = abi.decode( + actionData, + (ISignatureGateway.SetUsingAsCollateralAction) + ); + return + keccak256( + abi.encode( + SET_USING_AS_COLLATERAL_ACTION_TYPEHASH, + action.spoke, + action.reserveId, + action.useAsCollateral + ) + ); + } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) { + ISignatureGateway.UpdateUserRiskPremiumAction memory action = abi.decode( + actionData, + (ISignatureGateway.UpdateUserRiskPremiumAction) + ); + return keccak256(abi.encode(UPDATE_USER_RISK_PREMIUM_ACTION_TYPEHASH, action.spoke)); + } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) { + ISignatureGateway.UpdateUserDynamicConfigAction memory action = abi.decode( + actionData, + (ISignatureGateway.UpdateUserDynamicConfigAction) + ); + return keccak256(abi.encode(UPDATE_USER_DYNAMIC_CONFIG_ACTION_TYPEHASH, action.spoke)); + } else { + revert('BatchEIP712: invalid action type'); + } + } + + /// @notice Compute the full batch struct hash. + /// @dev Per EIP-712, struct hash = keccak256(abi.encode(typeHash, member1, member2, ...)) + /// For nested structs, each member is the hashStruct of that nested struct. + /// @param actionTypes Array of action type enum values. + /// @param actionData Array of ABI-encoded action structs. + /// @param onBehalfOf The user on whose behalf all actions are performed. + /// @param nonce The nonce for replay protection. + /// @param deadline The deadline for the batch. + /// @return The struct hash of the batch. + function hashBatch( + uint8[] memory actionTypes, + bytes[] memory actionData, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) internal pure returns (bytes32) { + bytes32 typeHash = buildBatchTypeHash(actionTypes); + + // Build the encoding dynamically since number of action fields varies + // Per EIP-712: each nested struct field is encoded as hashStruct(field) + bytes memory encoded = abi.encode(typeHash); + + uint256 len = actionTypes.length; + for (uint256 i = 0; i < len; i++) { + encoded = bytes.concat(encoded, abi.encode(hashAction(actionTypes[i], actionData[i]))); + } + + // Append the fixed fields + encoded = bytes.concat(encoded, abi.encode(onBehalfOf, nonce, deadline)); + + return keccak256(encoded); + } + + /// @notice Get the action type name string for a given action type. + /// @param actionType The action type enum value. + /// @return The action type name string. + function _getActionTypeName(uint8 actionType) private pure returns (string memory) { + if (actionType == uint8(ISignatureGateway.ActionType.Supply)) { + return SUPPLY_ACTION_NAME; + } else if (actionType == uint8(ISignatureGateway.ActionType.Withdraw)) { + return WITHDRAW_ACTION_NAME; + } else if (actionType == uint8(ISignatureGateway.ActionType.Borrow)) { + return BORROW_ACTION_NAME; + } else if (actionType == uint8(ISignatureGateway.ActionType.Repay)) { + return REPAY_ACTION_NAME; + } else if (actionType == uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) { + return SET_USING_AS_COLLATERAL_ACTION_NAME; + } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) { + return UPDATE_USER_RISK_PREMIUM_ACTION_NAME; + } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) { + return UPDATE_USER_DYNAMIC_CONFIG_ACTION_NAME; + } else { + revert('BatchEIP712: invalid action type'); + } + } + + /// @notice Convert a uint256 to its string representation. + /// @dev Only handles small numbers (0-9) for action indices. + /// @param value The value to convert. + /// @return The string representation. + function _uintToString(uint256 value) private pure returns (string memory) { + if (value == 0) return '0'; + if (value == 1) return '1'; + if (value == 2) return '2'; + if (value == 3) return '3'; + if (value == 4) return '4'; + if (value == 5) return '5'; + if (value == 6) return '6'; + if (value == 7) return '7'; + if (value == 8) return '8'; + if (value == 9) return '9'; + + // For values >= 10, build the string + bytes memory buffer; + while (value > 0) { + buffer = bytes.concat(bytes1(uint8(48 + (value % 10))), buffer); + value /= 10; + } + return string(buffer); + } +} diff --git a/tests/gas/Gateways.Operations.gas.t.sol b/tests/gas/Gateways.Operations.gas.t.sol index f21add918..bb277fff5 100644 --- a/tests/gas/Gateways.Operations.gas.t.sol +++ b/tests/gas/Gateways.Operations.gas.t.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.0; import 'tests/Base.t.sol'; import 'tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol'; import 'tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Base.t.sol'; +import {BatchEIP712} from 'src/position-manager/libraries/BatchEIP712.sol'; /// forge-config: default.isolate = true contract NativeTokenGateway_Gas_Tests is Base { @@ -262,6 +263,289 @@ contract SignatureGateway_Gas_Tests is SignatureGatewayBaseTest { } } +/// forge-config: default.isolate = true +contract SignatureGatewayBatch_Gas_Tests is SignatureGatewayBaseTest { + using BatchEIP712 for *; + + string internal NAMESPACE = 'SignatureGateway.BatchOperations'; + uint192 internal nonceKey = 0; + + function setUp() public virtual override { + super.setUp(); + vm.prank(SPOKE_ADMIN); + spoke1.updatePositionManager(address(gateway), true); + vm.prank(alice); + spoke1.setUserPositionManager(address(gateway), true); + vm.prank(alice); + gateway.useNonce(nonceKey); + } + + function _getBatchDigest( + uint8[] memory actionTypes, + bytes[] memory actionData, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) internal view returns (bytes32) { + bytes32 structHash = BatchEIP712.hashBatch( + actionTypes, + actionData, + onBehalfOf, + nonce, + deadline + ); + return keccak256(abi.encodePacked('\x19\x01', gateway.DOMAIN_SEPARATOR(), structHash)); + } + + function test_executeBatchWithSig_singleSupply() public { + uint256 reserveId = _wethReserveId(spoke1); + uint256 amount = 100e18; + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, nonceKey); + + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + + bytes[] memory actionData = new bytes[](1); + actionData[0] = abi.encode( + ISignatureGateway.SupplyAction({spoke: address(spoke1), reserveId: reserveId, amount: amount}) + ); + + bytes memory signature = _sign( + alicePk, + _getBatchDigest(actionTypes, actionData, alice, nonce, deadline) + ); + Utils.approve(spoke1, reserveId, alice, address(gateway), amount); + Utils.supply(spoke1, reserveId, alice, amount, alice); + + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + vm.snapshotGasLastCall(NAMESPACE, 'executeBatchWithSig: 1 action (supply)'); + } + + function test_executeBatchWithSig_supplyAndSetCollateral() public { + uint256 reserveId = _wethReserveId(spoke1); + uint256 amount = 100e18; + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, nonceKey); + + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode( + ISignatureGateway.SupplyAction({spoke: address(spoke1), reserveId: reserveId, amount: amount}) + ); + actionData[1] = abi.encode( + ISignatureGateway.SetUsingAsCollateralAction({ + spoke: address(spoke1), + reserveId: reserveId, + useAsCollateral: true + }) + ); + + bytes memory signature = _sign( + alicePk, + _getBatchDigest(actionTypes, actionData, alice, nonce, deadline) + ); + Utils.approve(spoke1, reserveId, alice, address(gateway), amount); + Utils.supplyCollateral(spoke1, reserveId, alice, amount, alice); + + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + vm.snapshotGasLastCall(NAMESPACE, 'executeBatchWithSig: 2 actions (supply+setCollateral)'); + } + + function test_executeBatchWithSig_supplyWithdraw() public { + uint256 reserveId = _wethReserveId(spoke1); + uint256 supplyAmount = 100e18; + uint256 withdrawAmount = 50e18; + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, nonceKey); + + Utils.supply(spoke1, reserveId, alice, supplyAmount * 2, alice); + + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode( + ISignatureGateway.SupplyAction({ + spoke: address(spoke1), + reserveId: reserveId, + amount: supplyAmount + }) + ); + actionData[1] = abi.encode( + ISignatureGateway.WithdrawAction({ + spoke: address(spoke1), + reserveId: reserveId, + amount: withdrawAmount + }) + ); + + bytes memory signature = _sign( + alicePk, + _getBatchDigest(actionTypes, actionData, alice, nonce, deadline) + ); + Utils.approve(spoke1, reserveId, alice, address(gateway), supplyAmount); + Utils.withdraw(spoke1, reserveId, alice, withdrawAmount, alice); + + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + vm.snapshotGasLastCall(NAMESPACE, 'executeBatchWithSig: 2 actions (supply+withdraw)'); + } + + function test_executeBatchWithSig_borrowRepay() public { + uint256 reserveId = _wethReserveId(spoke1); + uint256 borrowAmount = 100e18; + uint256 repayAmount = 50e18; + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, nonceKey); + + Utils.supplyCollateral(spoke1, reserveId, alice, borrowAmount * 10, alice); + Utils.borrow(spoke1, reserveId, alice, borrowAmount, alice); + + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Borrow); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Repay); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode( + ISignatureGateway.BorrowAction({ + spoke: address(spoke1), + reserveId: reserveId, + amount: borrowAmount + }) + ); + actionData[1] = abi.encode( + ISignatureGateway.RepayAction({ + spoke: address(spoke1), + reserveId: reserveId, + amount: repayAmount + }) + ); + + bytes memory signature = _sign( + alicePk, + _getBatchDigest(actionTypes, actionData, alice, nonce, deadline) + ); + Utils.approve(spoke1, reserveId, alice, address(gateway), repayAmount); + Utils.repay(spoke1, reserveId, alice, repayAmount, alice); + + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + vm.snapshotGasLastCall(NAMESPACE, 'executeBatchWithSig: 2 actions (borrow+repay)'); + } + + function test_executeBatchWithSig_threeActions() public { + uint256 reserveId = _wethReserveId(spoke1); + uint256 amount = 100e18; + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, nonceKey); + + Utils.supply(spoke1, reserveId, alice, amount * 3, alice); + + uint8[] memory actionTypes = new uint8[](3); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); + actionTypes[2] = uint8(ISignatureGateway.ActionType.Withdraw); + + bytes[] memory actionData = new bytes[](3); + actionData[0] = abi.encode( + ISignatureGateway.SupplyAction({spoke: address(spoke1), reserveId: reserveId, amount: amount}) + ); + actionData[1] = abi.encode( + ISignatureGateway.SetUsingAsCollateralAction({ + spoke: address(spoke1), + reserveId: reserveId, + useAsCollateral: true + }) + ); + actionData[2] = abi.encode( + ISignatureGateway.WithdrawAction({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount / 2 + }) + ); + + bytes memory signature = _sign( + alicePk, + _getBatchDigest(actionTypes, actionData, alice, nonce, deadline) + ); + Utils.approve(spoke1, reserveId, alice, address(gateway), amount); + Utils.setUsingAsCollateral(spoke1, reserveId, alice, true, alice); + Utils.withdraw(spoke1, reserveId, alice, amount / 2, alice); + + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + vm.snapshotGasLastCall( + NAMESPACE, + 'executeBatchWithSig: 3 actions (supply+setCollateral+withdraw)' + ); + } + + function test_executeBatchWithSig_fiveActions() public { + uint256 reserveId = _wethReserveId(spoke1); + uint256 amount = 100e18; + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, nonceKey); + + Utils.supplyCollateral(spoke1, reserveId, alice, amount * 10, alice); + Utils.borrow(spoke1, reserveId, alice, amount, alice); + + uint8[] memory actionTypes = new uint8[](5); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); + actionTypes[2] = uint8(ISignatureGateway.ActionType.Borrow); + actionTypes[3] = uint8(ISignatureGateway.ActionType.Repay); + actionTypes[4] = uint8(ISignatureGateway.ActionType.Withdraw); + + bytes[] memory actionData = new bytes[](5); + actionData[0] = abi.encode( + ISignatureGateway.SupplyAction({spoke: address(spoke1), reserveId: reserveId, amount: amount}) + ); + actionData[1] = abi.encode( + ISignatureGateway.SetUsingAsCollateralAction({ + spoke: address(spoke1), + reserveId: reserveId, + useAsCollateral: true + }) + ); + actionData[2] = abi.encode( + ISignatureGateway.BorrowAction({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount / 2 + }) + ); + actionData[3] = abi.encode( + ISignatureGateway.RepayAction({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount / 4 + }) + ); + actionData[4] = abi.encode( + ISignatureGateway.WithdrawAction({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount / 4 + }) + ); + + bytes memory signature = _sign( + alicePk, + _getBatchDigest(actionTypes, actionData, alice, nonce, deadline) + ); + Utils.approve(spoke1, reserveId, alice, address(gateway), amount + amount / 4); + Utils.borrow(spoke1, reserveId, alice, amount / 2, alice); + Utils.repay(spoke1, reserveId, alice, amount / 4, alice); + Utils.withdraw(spoke1, reserveId, alice, amount / 4, alice); + + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + vm.snapshotGasLastCall(NAMESPACE, 'executeBatchWithSig: 5 actions'); + } +} + /// forge-config: default.isolate = true contract SignatureGatewayPermit2_Gas_Tests is SignatureGatewayPermit2BaseTest { string internal NAMESPACE = 'SignatureGateway.Operations'; diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Batch.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Batch.t.sol new file mode 100644 index 000000000..017340de2 --- /dev/null +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Batch.t.sol @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: UNLICENSED +// Copyright (c) 2025 Aave Labs +pragma solidity ^0.8.0; + +import 'tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol'; +import {BatchEIP712} from 'src/position-manager/libraries/BatchEIP712.sol'; +import {ISpokeBase} from 'src/spoke/interfaces/ISpokeBase.sol'; + +contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { + using SafeCast for *; + + function setUp() public virtual override { + super.setUp(); + vm.prank(SPOKE_ADMIN); + spoke1.updatePositionManager(address(gateway), true); + vm.prank(alice); + spoke1.setUserPositionManager(address(gateway), true); + + assertTrue(spoke1.isPositionManagerActive(address(gateway))); + assertTrue(spoke1.isPositionManager(alice, address(gateway))); + } + + function _supplyActionData( + ISpoke spoke, + uint256 reserveId, + uint256 amount + ) internal view returns (ISignatureGateway.SupplyAction memory) { + return + ISignatureGateway.SupplyAction({spoke: address(spoke), reserveId: reserveId, amount: amount}); + } + + function _withdrawActionData( + ISpoke spoke, + uint256 reserveId, + uint256 amount + ) internal view returns (ISignatureGateway.WithdrawAction memory) { + return + ISignatureGateway.WithdrawAction({ + spoke: address(spoke), + reserveId: reserveId, + amount: amount + }); + } + + function _borrowActionData( + ISpoke spoke, + uint256 reserveId, + uint256 amount + ) internal view returns (ISignatureGateway.BorrowAction memory) { + return + ISignatureGateway.BorrowAction({spoke: address(spoke), reserveId: reserveId, amount: amount}); + } + + function _repayActionData( + ISpoke spoke, + uint256 reserveId, + uint256 amount + ) internal view returns (ISignatureGateway.RepayAction memory) { + return + ISignatureGateway.RepayAction({spoke: address(spoke), reserveId: reserveId, amount: amount}); + } + + function _setUsingAsCollateralActionData( + ISpoke spoke, + uint256 reserveId, + bool useAsCollateral + ) internal view returns (ISignatureGateway.SetUsingAsCollateralAction memory) { + return + ISignatureGateway.SetUsingAsCollateralAction({ + spoke: address(spoke), + reserveId: reserveId, + useAsCollateral: useAsCollateral + }); + } + + function _getBatchTypedDataHash( + ISignatureGateway _gateway, + uint8[] memory actionTypes, + bytes[] memory actionData, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) internal view returns (bytes32) { + bytes32 structHash = BatchEIP712.hashBatch( + actionTypes, + actionData, + onBehalfOf, + nonce, + deadline + ); + return keccak256(abi.encodePacked('\x19\x01', _gateway.DOMAIN_SEPARATOR(), structHash)); + } + + function test_executeBatchWithSig_singleSupply() public { + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 reserveId = _randomReserveId(spoke1); + uint256 amount = 1e18; + uint256 nonce = gateway.nonces(alice, _randomNonceKey()); + + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + + bytes[] memory actionData = new bytes[](1); + actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, amount)); + + bytes32 digest = _getBatchTypedDataHash( + gateway, + actionTypes, + actionData, + alice, + nonce, + deadline + ); + bytes memory signature = _sign(alicePk, digest); + + Utils.approve(spoke1, reserveId, alice, address(gateway), amount); + + vm.expectCall(address(spoke1), abi.encodeCall(ISpokeBase.supply, (reserveId, amount, alice))); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + + _assertNonceIncrement(gateway, alice, nonce); + _assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice); + _assertGatewayHasNoActivePosition(spoke1, gateway); + } + + function test_executeBatchWithSig_supplyAndWithdraw() public { + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 reserveId = _randomReserveId(spoke1); + uint256 supplyAmount = 2e18; + uint256 withdrawAmount = 1e18; + uint256 nonce = gateway.nonces(alice, _randomNonceKey()); + + Utils.supply(spoke1, reserveId, alice, supplyAmount + 1e18, alice); + + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, supplyAmount)); + actionData[1] = abi.encode(_withdrawActionData(spoke1, reserveId, withdrawAmount)); + + bytes32 digest = _getBatchTypedDataHash( + gateway, + actionTypes, + actionData, + alice, + nonce, + deadline + ); + bytes memory signature = _sign(alicePk, digest); + + Utils.approve(spoke1, reserveId, alice, address(gateway), supplyAmount); + + vm.expectCall( + address(spoke1), + abi.encodeCall(ISpokeBase.supply, (reserveId, supplyAmount, alice)) + ); + vm.expectCall( + address(spoke1), + abi.encodeCall(ISpokeBase.withdraw, (reserveId, withdrawAmount, alice)) + ); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + + _assertNonceIncrement(gateway, alice, nonce); + _assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice); + _assertGatewayHasNoActivePosition(spoke1, gateway); + } + + function test_executeBatchWithSig_supplyAndSetCollateral() public { + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 reserveId = _daiReserveId(spoke1); + uint256 amount = 1e18; + uint256 nonce = gateway.nonces(alice, _randomNonceKey()); + + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, amount)); + actionData[1] = abi.encode(_setUsingAsCollateralActionData(spoke1, reserveId, true)); + + bytes32 digest = _getBatchTypedDataHash( + gateway, + actionTypes, + actionData, + alice, + nonce, + deadline + ); + bytes memory signature = _sign(alicePk, digest); + + Utils.approve(spoke1, reserveId, alice, address(gateway), amount); + + vm.expectCall(address(spoke1), abi.encodeCall(ISpokeBase.supply, (reserveId, amount, alice))); + vm.expectCall( + address(spoke1), + abi.encodeCall(ISpoke.setUsingAsCollateral, (reserveId, true, alice)) + ); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + + _assertNonceIncrement(gateway, alice, nonce); + assertTrue(_isUsingAsCollateral(spoke1, reserveId, alice)); + _assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice); + _assertGatewayHasNoActivePosition(spoke1, gateway); + } + + function test_executeBatchWithSig_supplyBorrowRepay() public { + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 reserveId = _daiReserveId(spoke1); + uint256 supplyAmount = 10e18; + uint256 borrowAmount = 5e18; + uint256 repayAmount = 2e18; + uint256 nonce = gateway.nonces(alice, _randomNonceKey()); + + Utils.supplyCollateral(spoke1, reserveId, alice, supplyAmount, alice); + + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Borrow); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Repay); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode(_borrowActionData(spoke1, reserveId, borrowAmount)); + actionData[1] = abi.encode(_repayActionData(spoke1, reserveId, repayAmount)); + + bytes32 digest = _getBatchTypedDataHash( + gateway, + actionTypes, + actionData, + alice, + nonce, + deadline + ); + bytes memory signature = _sign(alicePk, digest); + + Utils.approve(spoke1, reserveId, alice, address(gateway), repayAmount); + + vm.expectCall( + address(spoke1), + abi.encodeCall(ISpokeBase.borrow, (reserveId, borrowAmount, alice)) + ); + vm.expectCall( + address(spoke1), + abi.encodeCall(ISpokeBase.repay, (reserveId, repayAmount, alice)) + ); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + + _assertNonceIncrement(gateway, alice, nonce); + _assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice); + _assertGatewayHasNoActivePosition(spoke1, gateway); + } + + function test_executeBatchWithSig_revertsOnExpiredDeadline() public { + uint256 deadline = vm.getBlockTimestamp() - 1; // Expired + uint256 reserveId = _randomReserveId(spoke1); + uint256 amount = 1e18; + uint256 nonce = gateway.nonces(alice, _randomNonceKey()); + + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + + bytes[] memory actionData = new bytes[](1); + actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, amount)); + + bytes32 digest = _getBatchTypedDataHash( + gateway, + actionTypes, + actionData, + alice, + nonce, + deadline + ); + bytes memory signature = _sign(alicePk, digest); + + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + } + + function test_executeBatchWithSig_revertsOnInvalidSignature() public { + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 reserveId = _randomReserveId(spoke1); + uint256 amount = 1e18; + uint256 nonce = gateway.nonces(alice, _randomNonceKey()); + + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + + bytes[] memory actionData = new bytes[](1); + actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, amount)); + + // Sign with wrong key + (, uint256 wrongPk) = makeAddrAndKey('wrong'); + bytes32 digest = _getBatchTypedDataHash( + gateway, + actionTypes, + actionData, + alice, + nonce, + deadline + ); + bytes memory signature = _sign(wrongPk, digest); + + vm.expectRevert(IIntentConsumer.InvalidSignature.selector); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + } + + function test_executeBatchWithSig_revertsOnLengthMismatch() public { + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, 0); + + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + + bytes[] memory actionData = new bytes[](1); // Mismatch! + actionData[0] = abi.encode(_supplyActionData(spoke1, 0, 1e18)); + + // Don't compute digest - length mismatch should revert before signature verification + vm.expectRevert(IGatewayBase.LengthMismatch.selector); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, hex'00'); + } + + function test_executeBatchWithSig_revertsOnEmptyBatch() public { + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, _randomNonceKey()); + + uint8[] memory actionTypes = new uint8[](0); + bytes[] memory actionData = new bytes[](0); + + bytes32 digest = _getBatchTypedDataHash( + gateway, + actionTypes, + actionData, + alice, + nonce, + deadline + ); + bytes memory signature = _sign(alicePk, digest); + + vm.expectRevert(IGatewayBase.InvalidBatchSize.selector); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, signature); + } + + function test_executeBatchWithSig_revertsOnInvalidActionType() public { + uint256 deadline = _warpBeforeRandomDeadline(); + uint256 nonce = gateway.nonces(alice, _randomNonceKey()); + + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = 255; // Invalid action type + + bytes[] memory actionData = new bytes[](1); + actionData[0] = abi.encode(_supplyActionData(spoke1, 0, 1e18)); + + // Note: We can't easily create a valid signature for invalid action type + // because the type string construction will fail + vm.expectRevert(); + vm.prank(vm.randomAddress()); + gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, hex'00'); + } + + function test_buildBatchTypeString_singleSupply() public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(SupplyAction action0,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyAction(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_supplyWithdraw() public pure { + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(SupplyAction action0,WithdrawAction action1,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyAction(address spoke,uint256 reserveId,uint256 amount)WithdrawAction(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_duplicateActions() public pure { + uint8[] memory actionTypes = new uint8[](3); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[2] = uint8(ISignatureGateway.ActionType.Withdraw); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + // SupplyAction definition should only appear once + assertEq( + typeString, + 'Batch(SupplyAction action0,SupplyAction action1,WithdrawAction action2,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyAction(address spoke,uint256 reserveId,uint256 amount)WithdrawAction(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_allActions() public pure { + uint8[] memory actionTypes = new uint8[](7); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + actionTypes[2] = uint8(ISignatureGateway.ActionType.Borrow); + actionTypes[3] = uint8(ISignatureGateway.ActionType.Repay); + actionTypes[4] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); + actionTypes[5] = uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium); + actionTypes[6] = uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + + // Type definitions should be in alphabetical order + string + memory expected = 'Batch(SupplyAction action0,WithdrawAction action1,BorrowAction action2,RepayAction action3,SetUsingAsCollateralAction action4,UpdateUserRiskPremiumAction action5,UpdateUserDynamicConfigAction action6,address onBehalfOf,uint256 nonce,uint256 deadline)BorrowAction(address spoke,uint256 reserveId,uint256 amount)RepayAction(address spoke,uint256 reserveId,uint256 amount)SetUsingAsCollateralAction(address spoke,uint256 reserveId,bool useAsCollateral)SupplyAction(address spoke,uint256 reserveId,uint256 amount)UpdateUserDynamicConfigAction(address spoke)UpdateUserRiskPremiumAction(address spoke)WithdrawAction(address spoke,uint256 reserveId,uint256 amount)'; + assertEq(typeString, expected); + } +} From 8a8e672a559fe59543759809411109b788568a83 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 20 Jan 2026 20:31:07 +0530 Subject: [PATCH 2/2] feat: improve tests and reuse action types --- .../SignatureGateway.BatchOperations.json | 12 +- snapshots/SignatureGateway.Operations.json | 20 +- src/position-manager/SignatureGateway.sol | 375 ++++----- .../interfaces/ISignatureGateway.sol | 164 ++-- .../libraries/BatchEIP712.sol | 200 ++--- src/position-manager/libraries/EIP712Hash.sol | 182 +++-- tests/gas/Gateways.Operations.gas.t.sol | 250 +++--- tests/mocks/EIP712Types.sol | 95 ++- tests/mocks/JsonBindings.sol | 714 +++++++++++++++--- tests/unit/misc/BatchEIP712.t.sol | 436 +++++++++++ tests/unit/misc/EIP712Hash.t.sol | 288 +++---- .../SignatureGateway.Base.t.sol | 322 ++++++-- .../SignatureGateway.Batch.t.sol | 151 +--- .../SignatureGateway.Constants.t.sol | 51 +- .../SignatureGateway.Permit2.Base.t.sol | 171 +++-- .../SignatureGateway.Permit2.Reverts.t.sol | 174 +++-- .../SignatureGateway.Permit2.t.sol | 44 +- ...ateway.Reverts.InsufficientAllowance.t.sol | 16 +- ...tureGateway.Reverts.InvalidSignature.t.sol | 94 ++- ...reGateway.Reverts.SpokeNotRegistered.t.sol | 14 +- ...ignatureGateway.Reverts.Unauthorized.t.sol | 34 +- .../SignatureGateway/SignatureGateway.t.sol | 111 +-- 22 files changed, 2517 insertions(+), 1401 deletions(-) create mode 100644 tests/unit/misc/BatchEIP712.t.sol diff --git a/snapshots/SignatureGateway.BatchOperations.json b/snapshots/SignatureGateway.BatchOperations.json index ed59359fa..f89cee2fd 100644 --- a/snapshots/SignatureGateway.BatchOperations.json +++ b/snapshots/SignatureGateway.BatchOperations.json @@ -1,8 +1,8 @@ { - "executeBatchWithSig: 1 action (supply)": "157129", - "executeBatchWithSig: 2 actions (borrow+repay)": "280249", - "executeBatchWithSig: 2 actions (supply+setCollateral)": "166166", - "executeBatchWithSig: 2 actions (supply+withdraw)": "206147", - "executeBatchWithSig: 3 actions (supply+setCollateral+withdraw)": "234510", - "executeBatchWithSig: 5 actions": "436106" + "executeBatchWithSig: 1 action (supply)": "157175", + "executeBatchWithSig: 2 actions (borrow+repay)": "280312", + "executeBatchWithSig: 2 actions (supply+setCollateral)": "166211", + "executeBatchWithSig: 2 actions (supply+withdraw)": "206224", + "executeBatchWithSig: 3 actions (supply+setCollateral+withdraw)": "234576", + "executeBatchWithSig: 5 actions": "436224" } \ No newline at end of file diff --git a/snapshots/SignatureGateway.Operations.json b/snapshots/SignatureGateway.Operations.json index 2bcb00ac1..aff3f5d90 100644 --- a/snapshots/SignatureGateway.Operations.json +++ b/snapshots/SignatureGateway.Operations.json @@ -1,12 +1,12 @@ { - "borrowWithSig": "212769", - "repayWithPermit2": "213005", - "repayWithSig": "186322", - "setSelfAsUserPositionManagerWithSig": "75426", - "setUsingAsCollateralWithSig": "85373", - "supplyWithPermit2": "172908", - "supplyWithSig": "151863", - "updateUserDynamicConfigWithSig": "63079", - "updateUserRiskPremiumWithSig": "62040", - "withdrawWithSig": "130539" + "borrowWithSig": "213302", + "repayWithPermit2": "213547", + "repayWithSig": "186958", + "setSelfAsUserPositionManagerWithSig": "75407", + "setUsingAsCollateralWithSig": "85689", + "supplyWithPermit2": "176929", + "supplyWithSig": "152347", + "updateUserDynamicConfigWithSig": "63485", + "updateUserRiskPremiumWithSig": "62466", + "withdrawWithSig": "130956" } \ No newline at end of file diff --git a/src/position-manager/SignatureGateway.sol b/src/position-manager/SignatureGateway.sol index 8f50fe41d..85d7ecad8 100644 --- a/src/position-manager/SignatureGateway.sol +++ b/src/position-manager/SignatureGateway.sol @@ -26,11 +26,11 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, IntentConsumer, Mul /// @inheritdoc ISignatureGateway string public constant SUPPLY_PERMIT2_WITNESS_TYPE_STRING = - 'Supply witness)Supply(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)'; + 'SupplyAction witness)SupplyAction(address onBehalfOf,uint256 nonce,uint256 deadline,SupplyParams params)SupplyParams(address spoke,uint256 reserveId,uint256 amount)TokenPermissions(address token,uint256 amount)'; /// @inheritdoc ISignatureGateway string public constant REPAY_PERMIT2_WITNESS_TYPE_STRING = - 'Repay witness)Repay(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)'; + 'RepayAction witness)RepayAction(address onBehalfOf,uint256 nonce,uint256 deadline,RepayParams params)RepayParams(address spoke,uint256 reserveId,uint256 amount)TokenPermissions(address token,uint256 amount)'; /// @inheritdoc ISignatureGateway bytes32 public constant SUPPLY_TYPEHASH = EIP712Hash.SUPPLY_TYPEHASH; @@ -69,156 +69,119 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, IntentConsumer, Mul /// @inheritdoc ISignatureGateway function supplyWithSig( - Supply calldata params, + SupplyAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) returns (uint256, uint256) { - address spoke = params.spoke; - uint256 reserveId = params.reserveId; - address user = params.onBehalfOf; + ) external onlyRegisteredSpoke(action.params.spoke) returns (uint256, uint256) { + address user = action.onBehalfOf; _verifyAndConsumeIntent({ signer: user, - intentHash: params.hash(), - nonce: params.nonce, - deadline: params.deadline, + intentHash: action.hash(), + nonce: action.nonce, + deadline: action.deadline, signature: signature }); - IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); - underlying.safeTransferFrom(user, address(this), params.amount); - underlying.forceApprove(spoke, params.amount); - - return ISpoke(spoke).supply(reserveId, params.amount, user); + return _executeSupply(action.params, user); } /// @inheritdoc ISignatureGateway function withdrawWithSig( - Withdraw calldata params, + WithdrawAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) returns (uint256, uint256) { - address spoke = params.spoke; - uint256 reserveId = params.reserveId; - address user = params.onBehalfOf; + ) external onlyRegisteredSpoke(action.params.spoke) returns (uint256, uint256) { + address user = action.onBehalfOf; _verifyAndConsumeIntent({ signer: user, - intentHash: params.hash(), - nonce: params.nonce, - deadline: params.deadline, + intentHash: action.hash(), + nonce: action.nonce, + deadline: action.deadline, signature: signature }); - IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); - (uint256 withdrawnShares, uint256 withdrawnAmount) = ISpoke(spoke).withdraw( - reserveId, - params.amount, - user - ); - underlying.safeTransfer(user, withdrawnAmount); - - return (withdrawnShares, withdrawnAmount); + return _executeWithdraw(action.params, user); } /// @inheritdoc ISignatureGateway function borrowWithSig( - Borrow calldata params, + BorrowAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) returns (uint256, uint256) { - address spoke = params.spoke; - uint256 reserveId = params.reserveId; - address user = params.onBehalfOf; + ) external onlyRegisteredSpoke(action.params.spoke) returns (uint256, uint256) { + address user = action.onBehalfOf; _verifyAndConsumeIntent({ signer: user, - intentHash: params.hash(), - nonce: params.nonce, - deadline: params.deadline, + intentHash: action.hash(), + nonce: action.nonce, + deadline: action.deadline, signature: signature }); - IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); - (uint256 borrowedShares, uint256 borrowedAmount) = ISpoke(spoke).borrow( - reserveId, - params.amount, - user - ); - underlying.safeTransfer(user, borrowedAmount); - - return (borrowedShares, borrowedAmount); + return _executeBorrow(action.params, user); } /// @inheritdoc ISignatureGateway function repayWithSig( - Repay calldata params, + RepayAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) returns (uint256, uint256) { - address spoke = params.spoke; - uint256 reserveId = params.reserveId; - address user = params.onBehalfOf; + ) external onlyRegisteredSpoke(action.params.spoke) returns (uint256, uint256) { + address user = action.onBehalfOf; _verifyAndConsumeIntent({ signer: user, - intentHash: params.hash(), - nonce: params.nonce, - deadline: params.deadline, + intentHash: action.hash(), + nonce: action.nonce, + deadline: action.deadline, signature: signature }); - IERC20 underlying = IERC20(_getReserveUnderlying(spoke, reserveId)); - uint256 repayAmount = MathUtils.min( - params.amount, - ISpoke(spoke).getUserTotalDebt(reserveId, user) - ); - - underlying.safeTransferFrom(user, address(this), repayAmount); - underlying.forceApprove(spoke, repayAmount); - - return ISpoke(spoke).repay(reserveId, repayAmount, user); + return _executeRepay(action.params, user); } /// @inheritdoc ISignatureGateway function setUsingAsCollateralWithSig( - SetUsingAsCollateral calldata params, + SetUsingAsCollateralAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) { - address user = params.onBehalfOf; + ) external onlyRegisteredSpoke(action.params.spoke) { + address user = action.onBehalfOf; _verifyAndConsumeIntent({ signer: user, - intentHash: params.hash(), - nonce: params.nonce, - deadline: params.deadline, + intentHash: action.hash(), + nonce: action.nonce, + deadline: action.deadline, signature: signature }); - ISpoke(params.spoke).setUsingAsCollateral(params.reserveId, params.useAsCollateral, user); + _executeSetUsingAsCollateral(action.params, user); } /// @inheritdoc ISignatureGateway function updateUserRiskPremiumWithSig( - UpdateUserRiskPremium calldata params, + UpdateUserRiskPremiumAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) { + ) external onlyRegisteredSpoke(action.params.spoke) { _verifyAndConsumeIntent({ - signer: params.user, - intentHash: params.hash(), - nonce: params.nonce, - deadline: params.deadline, + signer: action.user, + intentHash: action.hash(), + nonce: action.nonce, + deadline: action.deadline, signature: signature }); - ISpoke(params.spoke).updateUserRiskPremium(params.user); + _executeUpdateUserRiskPremium(action.params, action.user); } /// @inheritdoc ISignatureGateway function updateUserDynamicConfigWithSig( - UpdateUserDynamicConfig calldata params, + UpdateUserDynamicConfigAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) { + ) external onlyRegisteredSpoke(action.params.spoke) { _verifyAndConsumeIntent({ - signer: params.user, - intentHash: params.hash(), - nonce: params.nonce, - deadline: params.deadline, + signer: action.user, + intentHash: action.hash(), + nonce: action.nonce, + deadline: action.deadline, signature: signature }); - ISpoke(params.spoke).updateUserDynamicConfig(params.user); + _executeUpdateUserDynamicConfig(action.params, action.user); } /// @inheritdoc ISignatureGateway @@ -273,54 +236,60 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, IntentConsumer, Mul /// @inheritdoc ISignatureGateway function supplyWithPermit2( ISignatureTransfer.PermitTransferFrom calldata permit, - Supply calldata params, + SupplyAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) returns (uint256, uint256) { - require(block.timestamp <= params.deadline, InvalidSignature()); - _useCheckedNonce(params.onBehalfOf, params.nonce); + ) external onlyRegisteredSpoke(action.params.spoke) returns (uint256, uint256) { + require(block.timestamp <= action.deadline, InvalidSignature()); + _useCheckedNonce(action.onBehalfOf, action.nonce); ISignatureTransfer(PERMIT2).permitWitnessTransferFrom( permit, - ISignatureTransfer.SignatureTransferDetails(address(this), params.amount), - params.onBehalfOf, - params.hash(), + ISignatureTransfer.SignatureTransferDetails(address(this), action.params.amount), + action.onBehalfOf, + action.hash(), SUPPLY_PERMIT2_WITNESS_TYPE_STRING, signature ); - address underlying = _getReserveUnderlying(params.spoke, params.reserveId); - IERC20(underlying).forceApprove(params.spoke, params.amount); + address underlying = _getReserveUnderlying(action.params.spoke, action.params.reserveId); + IERC20(underlying).forceApprove(action.params.spoke, action.params.amount); - return ISpoke(params.spoke).supply(params.reserveId, params.amount, params.onBehalfOf); + return + ISpoke(action.params.spoke).supply( + action.params.reserveId, + action.params.amount, + action.onBehalfOf + ); } /// @inheritdoc ISignatureGateway function repayWithPermit2( ISignatureTransfer.PermitTransferFrom calldata permit, - Repay calldata params, + RepayAction calldata action, bytes calldata signature - ) external onlyRegisteredSpoke(params.spoke) returns (uint256, uint256) { - require(block.timestamp <= params.deadline, InvalidSignature()); - _useCheckedNonce(params.onBehalfOf, params.nonce); + ) external onlyRegisteredSpoke(action.params.spoke) returns (uint256, uint256) { + require(block.timestamp <= action.deadline, InvalidSignature()); + _useCheckedNonce(action.onBehalfOf, action.nonce); uint256 repayAmount = MathUtils.min( - params.amount, - ISpoke(params.spoke).getUserTotalDebt(params.reserveId, params.onBehalfOf) + action.params.amount, + ISpoke(action.params.spoke).getUserTotalDebt(action.params.reserveId, action.onBehalfOf) ); ISignatureTransfer(PERMIT2).permitWitnessTransferFrom( permit, ISignatureTransfer.SignatureTransferDetails(address(this), repayAmount), - params.onBehalfOf, - params.hash(), + action.onBehalfOf, + action.hash(), REPAY_PERMIT2_WITNESS_TYPE_STRING, signature ); - address underlying = _getReserveUnderlying(params.spoke, params.reserveId); - IERC20(underlying).forceApprove(params.spoke, repayAmount); + address underlying = _getReserveUnderlying(action.params.spoke, action.params.reserveId); + IERC20(underlying).forceApprove(action.params.spoke, repayAmount); - return ISpoke(params.spoke).repay(params.reserveId, repayAmount, params.onBehalfOf); + return + ISpoke(action.params.spoke).repay(action.params.reserveId, repayAmount, action.onBehalfOf); } /// @inheritdoc ISignatureGateway @@ -352,143 +321,133 @@ contract SignatureGateway is ISignatureGateway, GatewayBase, IntentConsumer, Mul }); for (uint256 i = 0; i < len; i++) { - _executeAction(actionTypes[i], actionData[i], onBehalfOf); + _executeBatchAction(actionTypes[i], actionData[i], onBehalfOf); } } - /// @dev Execute a single action from the batch. - /// @param actionType The action type enum value. - /// @param actionData The ABI-encoded action struct. - /// @param onBehalfOf The user on whose behalf the action is performed. - function _executeAction(uint8 actionType, bytes memory actionData, address onBehalfOf) internal { - if (actionType == uint8(ISignatureGateway.ActionType.Supply)) { - _executeSupplyAction(actionData, onBehalfOf); - } else if (actionType == uint8(ISignatureGateway.ActionType.Withdraw)) { - _executeWithdrawAction(actionData, onBehalfOf); - } else if (actionType == uint8(ISignatureGateway.ActionType.Borrow)) { - _executeBorrowAction(actionData, onBehalfOf); - } else if (actionType == uint8(ISignatureGateway.ActionType.Repay)) { - _executeRepayAction(actionData, onBehalfOf); - } else if (actionType == uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) { - _executeSetUsingAsCollateralAction(actionData, onBehalfOf); - } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) { - _executeUpdateUserRiskPremiumAction(actionData, onBehalfOf); - } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) { - _executeUpdateUserDynamicConfigAction(actionData, onBehalfOf); + function _executeBatchAction( + uint8 actionType, + bytes memory actionData, + address onBehalfOf + ) internal { + if (actionType == uint8(ActionType.Supply)) { + SupplyParams memory params = abi.decode(actionData, (SupplyParams)); + _isSpokeValid(params.spoke); + _executeSupply(params, onBehalfOf); + } else if (actionType == uint8(ActionType.Withdraw)) { + WithdrawParams memory params = abi.decode(actionData, (WithdrawParams)); + _isSpokeValid(params.spoke); + _executeWithdraw(params, onBehalfOf); + } else if (actionType == uint8(ActionType.Borrow)) { + BorrowParams memory params = abi.decode(actionData, (BorrowParams)); + _isSpokeValid(params.spoke); + _executeBorrow(params, onBehalfOf); + } else if (actionType == uint8(ActionType.Repay)) { + RepayParams memory params = abi.decode(actionData, (RepayParams)); + _isSpokeValid(params.spoke); + _executeRepay(params, onBehalfOf); + } else if (actionType == uint8(ActionType.SetUsingAsCollateral)) { + SetUsingAsCollateralParams memory params = abi.decode( + actionData, + (SetUsingAsCollateralParams) + ); + _isSpokeValid(params.spoke); + _executeSetUsingAsCollateral(params, onBehalfOf); + } else if (actionType == uint8(ActionType.UpdateUserRiskPremium)) { + UpdateUserRiskPremiumParams memory params = abi.decode( + actionData, + (UpdateUserRiskPremiumParams) + ); + _isSpokeValid(params.spoke); + _executeUpdateUserRiskPremium(params, onBehalfOf); + } else if (actionType == uint8(ActionType.UpdateUserDynamicConfig)) { + UpdateUserDynamicConfigParams memory params = abi.decode( + actionData, + (UpdateUserDynamicConfigParams) + ); + _isSpokeValid(params.spoke); + _executeUpdateUserDynamicConfig(params, onBehalfOf); } else { revert InvalidActionType(); } } - /// @dev Execute a supply action. - function _executeSupplyAction(bytes memory actionData, address onBehalfOf) internal { - ISignatureGateway.SupplyAction memory action = abi.decode( - actionData, - (ISignatureGateway.SupplyAction) - ); - _isSpokeValid(action.spoke); - - IERC20 underlying = IERC20(_getReserveUnderlying(action.spoke, action.reserveId)); - underlying.safeTransferFrom(onBehalfOf, address(this), action.amount); - underlying.forceApprove(action.spoke, action.amount); + function _executeSupply( + SupplyParams memory params, + address onBehalfOf + ) internal returns (uint256, uint256) { + IERC20 underlying = IERC20(_getReserveUnderlying(params.spoke, params.reserveId)); + underlying.safeTransferFrom(onBehalfOf, address(this), params.amount); + underlying.forceApprove(params.spoke, params.amount); - ISpoke(action.spoke).supply(action.reserveId, action.amount, onBehalfOf); + return ISpoke(params.spoke).supply(params.reserveId, params.amount, onBehalfOf); } - /// @dev Execute a withdraw action. - function _executeWithdrawAction(bytes memory actionData, address onBehalfOf) internal { - ISignatureGateway.WithdrawAction memory action = abi.decode( - actionData, - (ISignatureGateway.WithdrawAction) - ); - _isSpokeValid(action.spoke); - - IERC20 underlying = IERC20(_getReserveUnderlying(action.spoke, action.reserveId)); - (, uint256 withdrawnAmount) = ISpoke(action.spoke).withdraw( - action.reserveId, - action.amount, + function _executeWithdraw( + WithdrawParams memory params, + address onBehalfOf + ) internal returns (uint256, uint256) { + IERC20 underlying = IERC20(_getReserveUnderlying(params.spoke, params.reserveId)); + (uint256 withdrawnShares, uint256 withdrawnAmount) = ISpoke(params.spoke).withdraw( + params.reserveId, + params.amount, onBehalfOf ); underlying.safeTransfer(onBehalfOf, withdrawnAmount); - } - /// @dev Execute a borrow action. - function _executeBorrowAction(bytes memory actionData, address onBehalfOf) internal { - ISignatureGateway.BorrowAction memory action = abi.decode( - actionData, - (ISignatureGateway.BorrowAction) - ); - _isSpokeValid(action.spoke); + return (withdrawnShares, withdrawnAmount); + } - IERC20 underlying = IERC20(_getReserveUnderlying(action.spoke, action.reserveId)); - (, uint256 borrowedAmount) = ISpoke(action.spoke).borrow( - action.reserveId, - action.amount, + function _executeBorrow( + BorrowParams memory params, + address onBehalfOf + ) internal returns (uint256, uint256) { + IERC20 underlying = IERC20(_getReserveUnderlying(params.spoke, params.reserveId)); + (uint256 borrowedShares, uint256 borrowedAmount) = ISpoke(params.spoke).borrow( + params.reserveId, + params.amount, onBehalfOf ); underlying.safeTransfer(onBehalfOf, borrowedAmount); - } - /// @dev Execute a repay action. - function _executeRepayAction(bytes memory actionData, address onBehalfOf) internal { - ISignatureGateway.RepayAction memory action = abi.decode( - actionData, - (ISignatureGateway.RepayAction) - ); - _isSpokeValid(action.spoke); + return (borrowedShares, borrowedAmount); + } - IERC20 underlying = IERC20(_getReserveUnderlying(action.spoke, action.reserveId)); + function _executeRepay( + RepayParams memory params, + address onBehalfOf + ) internal returns (uint256, uint256) { + IERC20 underlying = IERC20(_getReserveUnderlying(params.spoke, params.reserveId)); uint256 repayAmount = MathUtils.min( - action.amount, - ISpoke(action.spoke).getUserTotalDebt(action.reserveId, onBehalfOf) + params.amount, + ISpoke(params.spoke).getUserTotalDebt(params.reserveId, onBehalfOf) ); underlying.safeTransferFrom(onBehalfOf, address(this), repayAmount); - underlying.forceApprove(action.spoke, repayAmount); + underlying.forceApprove(params.spoke, repayAmount); - ISpoke(action.spoke).repay(action.reserveId, repayAmount, onBehalfOf); + return ISpoke(params.spoke).repay(params.reserveId, repayAmount, onBehalfOf); } - /// @dev Execute a setUsingAsCollateral action. - function _executeSetUsingAsCollateralAction( - bytes memory actionData, + function _executeSetUsingAsCollateral( + SetUsingAsCollateralParams memory params, address onBehalfOf ) internal { - ISignatureGateway.SetUsingAsCollateralAction memory action = abi.decode( - actionData, - (ISignatureGateway.SetUsingAsCollateralAction) - ); - _isSpokeValid(action.spoke); - - ISpoke(action.spoke).setUsingAsCollateral(action.reserveId, action.useAsCollateral, onBehalfOf); + ISpoke(params.spoke).setUsingAsCollateral(params.reserveId, params.useAsCollateral, onBehalfOf); } - /// @dev Execute an updateUserRiskPremium action. - function _executeUpdateUserRiskPremiumAction( - bytes memory actionData, + function _executeUpdateUserRiskPremium( + UpdateUserRiskPremiumParams memory params, address onBehalfOf ) internal { - ISignatureGateway.UpdateUserRiskPremiumAction memory action = abi.decode( - actionData, - (ISignatureGateway.UpdateUserRiskPremiumAction) - ); - _isSpokeValid(action.spoke); - - ISpoke(action.spoke).updateUserRiskPremium(onBehalfOf); + ISpoke(params.spoke).updateUserRiskPremium(onBehalfOf); } - /// @dev Execute an updateUserDynamicConfig action. - function _executeUpdateUserDynamicConfigAction( - bytes memory actionData, + function _executeUpdateUserDynamicConfig( + UpdateUserDynamicConfigParams memory params, address onBehalfOf ) internal { - ISignatureGateway.UpdateUserDynamicConfigAction memory action = abi.decode( - actionData, - (ISignatureGateway.UpdateUserDynamicConfigAction) - ); - _isSpokeValid(action.spoke); - - ISpoke(action.spoke).updateUserDynamicConfig(onBehalfOf); + ISpoke(params.spoke).updateUserDynamicConfig(onBehalfOf); } function _domainNameAndVersion() internal pure override returns (string memory, string memory) { diff --git a/src/position-manager/interfaces/ISignatureGateway.sol b/src/position-manager/interfaces/ISignatureGateway.sol index 403b9de5e..118627369 100644 --- a/src/position-manager/interfaces/ISignatureGateway.sol +++ b/src/position-manager/interfaces/ISignatureGateway.sol @@ -22,181 +22,161 @@ interface ISignatureGateway is IGatewayBase, IIntentConsumer, IMulticall { UpdateUserDynamicConfig // 6 } - /// @notice Batch action for supplying assets (without nonce/deadline/onBehalfOf - lifted to batch level). + /// @notice Parameters for supplying assets. /// @param spoke The address of the registered spoke. /// @param reserveId The identifier of the reserve. /// @param amount The amount of assets to supply. - struct SupplyAction { + struct SupplyParams { address spoke; uint256 reserveId; uint256 amount; } - /// @notice Batch action for withdrawing assets. + /// @notice Parameters for withdrawing assets. /// @param spoke The address of the registered spoke. /// @param reserveId The identifier of the reserve. /// @param amount The amount of assets to withdraw. - struct WithdrawAction { + struct WithdrawParams { address spoke; uint256 reserveId; uint256 amount; } - /// @notice Batch action for borrowing assets. + /// @notice Parameters for borrowing assets. /// @param spoke The address of the registered spoke. /// @param reserveId The identifier of the reserve. /// @param amount The amount of assets to borrow. - struct BorrowAction { + struct BorrowParams { address spoke; uint256 reserveId; uint256 amount; } - /// @notice Batch action for repaying assets. + /// @notice Parameters for repaying assets. /// @param spoke The address of the registered spoke. /// @param reserveId The identifier of the reserve. /// @param amount The amount of assets to repay. - struct RepayAction { + struct RepayParams { address spoke; uint256 reserveId; uint256 amount; } - /// @notice Batch action for setting collateral usage. + /// @notice Parameters for setting collateral usage. /// @param spoke The address of the registered spoke. /// @param reserveId The identifier of the reserve. /// @param useAsCollateral True to enable the reserve as collateral, false to disable it. - struct SetUsingAsCollateralAction { + struct SetUsingAsCollateralParams { address spoke; uint256 reserveId; bool useAsCollateral; } - /// @notice Batch action for updating user risk premium. + /// @notice Parameters for updating user risk premium. /// @param spoke The address of the registered spoke. - struct UpdateUserRiskPremiumAction { + struct UpdateUserRiskPremiumParams { address spoke; } - /// @notice Batch action for updating user dynamic config. + /// @notice Parameters for updating user dynamic config. /// @param spoke The address of the registered spoke. - struct UpdateUserDynamicConfigAction { + struct UpdateUserDynamicConfigParams { address spoke; } - /// @notice Intent data to supply assets to a reserve. - /// @param spoke The address of the registered spoke. - /// @param reserveId The identifier of the reserve. - /// @param amount The amount of assets to supply. + /// @notice Intent action to supply assets to a reserve. /// @param onBehalfOf The address of the user on whose behalf the supply is performed. /// @param nonce The key-prefixed nonce for the signature. /// @param deadline The deadline for the intent. - struct Supply { - address spoke; - uint256 reserveId; - uint256 amount; + /// @param params The supply parameters. + struct SupplyAction { address onBehalfOf; uint256 nonce; uint256 deadline; + SupplyParams params; } - /// @notice Intent data to withdraw assets from a reserve. - /// @param spoke The address of the registered spoke. - /// @param reserveId The identifier of the reserve. - /// @param amount The amount of assets to withdraw. + /// @notice Intent action to withdraw assets from a reserve. /// @param onBehalfOf The address of the user on whose behalf the withdraw is performed. /// @param nonce The key-prefixed nonce for the signature. /// @param deadline The deadline for the intent. - struct Withdraw { - address spoke; - uint256 reserveId; - uint256 amount; + /// @param params The withdraw parameters. + struct WithdrawAction { address onBehalfOf; uint256 nonce; uint256 deadline; + WithdrawParams params; } - /// @notice Intent data to borrow assets from a reserve. - /// @param spoke The address of the registered spoke. - /// @param reserveId The identifier of the reserve. - /// @param amount The amount of assets to borrow. + /// @notice Intent action to borrow assets from a reserve. /// @param onBehalfOf The address of the user on whose behalf the borrow is performed. /// @param nonce The key-prefixed nonce for the signature. /// @param deadline The deadline for the intent. - struct Borrow { - address spoke; - uint256 reserveId; - uint256 amount; + /// @param params The borrow parameters. + struct BorrowAction { address onBehalfOf; uint256 nonce; uint256 deadline; + BorrowParams params; } - /// @notice Intent data to repay assets to a reserve. - /// @param spoke The address of the registered spoke. - /// @param reserveId The identifier of the reserve. - /// @param amount The amount of assets to repay. + /// @notice Intent action to repay assets to a reserve. /// @param onBehalfOf The address of the user on whose behalf the repay is performed. /// @param nonce The key-prefixed nonce for the signature. /// @param deadline The deadline for the intent. - struct Repay { - address spoke; - uint256 reserveId; - uint256 amount; + /// @param params The repay parameters. + struct RepayAction { address onBehalfOf; uint256 nonce; uint256 deadline; + RepayParams params; } - /// @notice Intent data to enable or disable a reserve as collateral. - /// @param spoke The address of the registered spoke. - /// @param reserveId The identifier of the reserve. - /// @param useAsCollateral True to enable the reserve as collateral, false to disable it. + /// @notice Intent action to enable or disable a reserve as collateral. /// @param onBehalfOf The address of the user on whose behalf the action is performed. /// @param nonce The key-prefixed nonce for the signature. /// @param deadline The deadline for the intent. - struct SetUsingAsCollateral { - address spoke; - uint256 reserveId; - bool useAsCollateral; + /// @param params The setUsingAsCollateral parameters. + struct SetUsingAsCollateralAction { address onBehalfOf; uint256 nonce; uint256 deadline; + SetUsingAsCollateralParams params; } - /// @notice Intent data to update the risk premium of a user position. - /// @param spoke The address of the registered spoke. + /// @notice Intent action to update the risk premium of a user position. /// @param user The address of the user whose risk premium is being updated. /// @param nonce The key-prefixed nonce for the signature. /// @param deadline The deadline for the intent. - struct UpdateUserRiskPremium { - address spoke; + /// @param params The updateUserRiskPremium parameters. + struct UpdateUserRiskPremiumAction { address user; uint256 nonce; uint256 deadline; + UpdateUserRiskPremiumParams params; } - /// @notice Intent data to update the dynamic configuration of a user position. - /// @param spoke The address of the registered spoke. + /// @notice Intent action to update the dynamic configuration of a user position. /// @param user The address of the user whose dynamic config is being updated. /// @param nonce The key-prefixed nonce for the signature. /// @param deadline The deadline for the intent. - struct UpdateUserDynamicConfig { - address spoke; + /// @param params The updateUserDynamicConfig parameters. + struct UpdateUserDynamicConfigAction { address user; uint256 nonce; uint256 deadline; + UpdateUserDynamicConfigParams params; } /// @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. - /// @param params The structured supply parameters. + /// @param action The structured supply action. /// @param signature The signed bytes for the intent. /// @return The amount of shares supplied. /// @return The amount of assets supplied. function supplyWithSig( - Supply calldata params, + SupplyAction calldata action, bytes calldata signature ) external returns (uint256, uint256); @@ -204,24 +184,24 @@ interface ISignatureGateway is IGatewayBase, IIntentConsumer, IMulticall { /// @dev Providing an amount exceeding the user's current withdrawable balance indicates a request for a maximum withdrawal. /// @dev Withdrawn assets are pushed to `onBehalfOf`. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. - /// @param params The structured withdraw parameters. + /// @param action The structured withdraw action. /// @param signature The signed bytes for the intent. /// @return The amount of shares withdrawn. /// @return The amount of assets withdrawn. function withdrawWithSig( - Withdraw calldata params, + WithdrawAction calldata action, bytes calldata signature ) external returns (uint256, uint256); /// @notice Facilitates `borrow` action on the specified registered `spoke` with a typed signature from `onBehalfOf`. /// @dev Borrowed assets are pushed to `onBehalfOf`. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. - /// @param params The structured borrow parameters. + /// @param action The structured borrow action. /// @param signature The signed bytes for the intent. /// @return The amount of shares borrowed. /// @return The amount of assets borrowed. function borrowWithSig( - Borrow calldata params, + BorrowAction calldata action, bytes calldata signature ) external returns (uint256, uint256); @@ -229,39 +209,39 @@ interface ISignatureGateway is IGatewayBase, IIntentConsumer, IMulticall { /// @dev Repay assets are pulled from `onBehalfOf`, prior approval to this gateway is required. /// @dev Providing an amount greater than the user's current debt indicates a request to repay the maximum possible amount. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. - /// @param params The structured repay parameters. + /// @param action The structured repay action. /// @param signature The signed bytes for the intent. /// @return The amount of shares repaid. /// @return The amount of assets repaid. function repayWithSig( - Repay calldata params, + RepayAction calldata action, bytes calldata signature ) external returns (uint256, uint256); /// @notice Facilitates `setUsingAsCollateral` action on the specified registered `spoke` with a typed signature from `onBehalfOf`. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. - /// @param params The structured setUsingAsCollateral parameters. + /// @param action The structured setUsingAsCollateral action. /// @param signature The signed bytes for the intent. function setUsingAsCollateralWithSig( - SetUsingAsCollateral calldata params, + SetUsingAsCollateralAction calldata action, bytes calldata signature ) external; /// @notice Facilitates `updateUserRiskPremium` action on the specified registered `spoke` with a typed signature from `user`. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. - /// @param params The structured updateUserRiskPremium parameters. + /// @param action The structured updateUserRiskPremium action. /// @param signature The signed bytes for the intent. function updateUserRiskPremiumWithSig( - UpdateUserRiskPremium calldata params, + UpdateUserRiskPremiumAction calldata action, bytes calldata signature ) external; /// @notice Facilitates `updateUserDynamicConfig` action on the specified registered `spoke` with a typed signature from `user`. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. - /// @param params The structured updateUserDynamicConfig parameters. + /// @param action The structured updateUserDynamicConfig action. /// @param signature The signed bytes for the intent. function updateUserDynamicConfigWithSig( - UpdateUserDynamicConfig calldata params, + UpdateUserDynamicConfigAction calldata action, bytes calldata signature ) external; @@ -305,30 +285,30 @@ interface ISignatureGateway is IGatewayBase, IIntentConsumer, IMulticall { /// @notice Facilitates `supply` action using Permit2's permitWitnessTransferFrom. /// @dev User must have approved Permit2 to spend their tokens. - /// @dev The Supply struct is used as the witness data in the Permit2 signature. + /// @dev The SupplyAction struct is used as the witness data in the Permit2 signature. /// @param permit The Permit2 transfer data signed over by the user. - /// @param params The structured supply parameters (used as witness). + /// @param action The structured supply action (used as witness). /// @param signature The Permit2 signature. /// @return The amount of shares supplied. /// @return The amount of assets supplied. function supplyWithPermit2( ISignatureTransfer.PermitTransferFrom calldata permit, - Supply calldata params, + SupplyAction calldata action, bytes calldata signature ) external returns (uint256, uint256); /// @notice Facilitates `repay` action using Permit2's permitWitnessTransferFrom. /// @dev User must have approved Permit2 to spend their tokens. - /// @dev The Repay struct is used as the witness data in the Permit2 signature. + /// @dev The RepayAction struct is used as the witness data in the Permit2 signature. /// @dev Providing an amount greater than the user's current debt indicates a request to repay the maximum possible amount. /// @param permit The Permit2 transfer data signed over by the user. - /// @param params The structured repay parameters (used as witness). + /// @param action The structured repay action (used as witness). /// @param signature The Permit2 signature. /// @return The amount of shares repaid. /// @return The amount of assets repaid. function repayWithPermit2( ISignatureTransfer.PermitTransferFrom calldata permit, - Repay calldata params, + RepayAction calldata action, bytes calldata signature ) external returns (uint256, uint256); @@ -337,7 +317,7 @@ interface ISignatureGateway is IGatewayBase, IIntentConsumer, IMulticall { /// @dev All actions in the batch are executed for the same `onBehalfOf` address. /// @dev Uses keyed-nonces where for each key's namespace nonce is consumed sequentially. /// @param actionTypes Array of action types (ActionType enum values). - /// @param actionData Array of ABI-encoded action structs corresponding to each action type. + /// @param actionData Array of ABI-encoded action params structs corresponding to each action type. /// @param onBehalfOf The address of the user on whose behalf all actions are performed. /// @param nonce The key-prefixed nonce for the batch signature. /// @param deadline The deadline for the batch intent. @@ -351,31 +331,31 @@ interface ISignatureGateway is IGatewayBase, IIntentConsumer, IMulticall { bytes calldata signature ) external; - /// @notice Returns the type hash for the Supply intent. + /// @notice Returns the type hash for the SupplyAction intent. function SUPPLY_TYPEHASH() external view returns (bytes32); - /// @notice Returns the type hash for the Withdraw intent. + /// @notice Returns the type hash for the WithdrawAction intent. function WITHDRAW_TYPEHASH() external view returns (bytes32); - /// @notice Returns the type hash for the Borrow intent. + /// @notice Returns the type hash for the BorrowAction intent. function BORROW_TYPEHASH() external view returns (bytes32); - /// @notice Returns the type hash for the Repay intent. + /// @notice Returns the type hash for the RepayAction intent. function REPAY_TYPEHASH() external view returns (bytes32); - /// @notice Returns the type hash for the SetUsingAsCollateral intent. + /// @notice Returns the type hash for the SetUsingAsCollateralAction intent. function SET_USING_AS_COLLATERAL_TYPEHASH() external view returns (bytes32); - /// @notice Returns the type hash for the UpdateUserRiskPremium intent. + /// @notice Returns the type hash for the UpdateUserRiskPremiumAction intent. function UPDATE_USER_RISK_PREMIUM_TYPEHASH() external view returns (bytes32); - /// @notice Returns the type hash for the UpdateUserDynamicConfig intent. + /// @notice Returns the type hash for the UpdateUserDynamicConfigAction intent. function UPDATE_USER_DYNAMIC_CONFIG_TYPEHASH() external view returns (bytes32); - /// @notice Returns the EIP-712 witness type string for Supply used with Permit2. + /// @notice Returns the EIP-712 witness type string for SupplyAction used with Permit2. function SUPPLY_PERMIT2_WITNESS_TYPE_STRING() external view returns (string memory); - /// @notice Returns the EIP-712 witness type string for Repay used with Permit2. + /// @notice Returns the EIP-712 witness type string for RepayAction used with Permit2. function REPAY_PERMIT2_WITNESS_TYPE_STRING() external view returns (string memory); /// @notice Returns the canonical Permit2 contract address. diff --git a/src/position-manager/libraries/BatchEIP712.sol b/src/position-manager/libraries/BatchEIP712.sol index 2b1036cf6..8aa49d0ff 100644 --- a/src/position-manager/libraries/BatchEIP712.sol +++ b/src/position-manager/libraries/BatchEIP712.sol @@ -9,56 +9,58 @@ import {ISignatureGateway} from 'src/position-manager/interfaces/ISignatureGatew /// @notice Helper methods to construct dynamic EIP-712 type strings and hash batch actions. /// @dev Constructs type strings at runtime to maintain full wallet UX visibility for batch signing. library BatchEIP712 { - string internal constant SUPPLY_ACTION_NAME = 'SupplyAction'; - string internal constant WITHDRAW_ACTION_NAME = 'WithdrawAction'; - string internal constant BORROW_ACTION_NAME = 'BorrowAction'; - string internal constant REPAY_ACTION_NAME = 'RepayAction'; - string internal constant SET_USING_AS_COLLATERAL_ACTION_NAME = 'SetUsingAsCollateralAction'; - string internal constant UPDATE_USER_RISK_PREMIUM_ACTION_NAME = 'UpdateUserRiskPremiumAction'; - string internal constant UPDATE_USER_DYNAMIC_CONFIG_ACTION_NAME = 'UpdateUserDynamicConfigAction'; - - string internal constant BORROW_ACTION_DEF = - 'BorrowAction(address spoke,uint256 reserveId,uint256 amount)'; - string internal constant REPAY_ACTION_DEF = - 'RepayAction(address spoke,uint256 reserveId,uint256 amount)'; - string internal constant SET_USING_AS_COLLATERAL_ACTION_DEF = - 'SetUsingAsCollateralAction(address spoke,uint256 reserveId,bool useAsCollateral)'; - string internal constant SUPPLY_ACTION_DEF = - 'SupplyAction(address spoke,uint256 reserveId,uint256 amount)'; - string internal constant UPDATE_USER_DYNAMIC_CONFIG_ACTION_DEF = - 'UpdateUserDynamicConfigAction(address spoke)'; - string internal constant UPDATE_USER_RISK_PREMIUM_ACTION_DEF = - 'UpdateUserRiskPremiumAction(address spoke)'; - string internal constant WITHDRAW_ACTION_DEF = - 'WithdrawAction(address spoke,uint256 reserveId,uint256 amount)'; - - bytes32 internal constant SUPPLY_ACTION_TYPEHASH = - // keccak256('SupplyAction(address spoke,uint256 reserveId,uint256 amount)') - 0x92108fb6c1c54e895857cadeb15a1d0ff251d05ab5bc45c397f7f0bf4513524f; - - bytes32 internal constant WITHDRAW_ACTION_TYPEHASH = - // keccak256('WithdrawAction(address spoke,uint256 reserveId,uint256 amount)') - 0x9886e55b7e2df773f3c842b4432b6e809e8669053f302bffdacc143660738090; - - bytes32 internal constant BORROW_ACTION_TYPEHASH = - // keccak256('BorrowAction(address spoke,uint256 reserveId,uint256 amount)') - 0x2d06ff6c841f7e36ccc14b960e3f08ce8e6eb41ab93b31281ffa7ad44e21026c; - - bytes32 internal constant REPAY_ACTION_TYPEHASH = - // keccak256('RepayAction(address spoke,uint256 reserveId,uint256 amount)') - 0xd0bfcdb753a3de34964385d0931b97edfc843abb115302d37b474e15a31220e4; - - bytes32 internal constant SET_USING_AS_COLLATERAL_ACTION_TYPEHASH = - // keccak256('SetUsingAsCollateralAction(address spoke,uint256 reserveId,bool useAsCollateral)') - 0x274cee27fcc6e1e5383183a5d15ceaba083b45ceb85fd75d497dc482b444ff89; - - bytes32 internal constant UPDATE_USER_RISK_PREMIUM_ACTION_TYPEHASH = - // keccak256('UpdateUserRiskPremiumAction(address spoke)') - 0x3d45bb6df468588f10dd8ff3cb2c53086fc7d49dc451b275b868a3c2b32e50d7; - - bytes32 internal constant UPDATE_USER_DYNAMIC_CONFIG_ACTION_TYPEHASH = - // keccak256('UpdateUserDynamicConfigAction(address spoke)') - 0x8548aa46ece028df12b6ecd855470cbc563c7599931c9c156ae140206e8853ca; + error InvalidActionType(uint8 actionType); + + string internal constant SUPPLY_PARAMS_NAME = 'SupplyParams'; + string internal constant WITHDRAW_PARAMS_NAME = 'WithdrawParams'; + string internal constant BORROW_PARAMS_NAME = 'BorrowParams'; + string internal constant REPAY_PARAMS_NAME = 'RepayParams'; + string internal constant SET_USING_AS_COLLATERAL_PARAMS_NAME = 'SetUsingAsCollateralParams'; + string internal constant UPDATE_USER_RISK_PREMIUM_PARAMS_NAME = 'UpdateUserRiskPremiumParams'; + string internal constant UPDATE_USER_DYNAMIC_CONFIG_PARAMS_NAME = 'UpdateUserDynamicConfigParams'; + + string internal constant BORROW_PARAMS_DEF = + 'BorrowParams(address spoke,uint256 reserveId,uint256 amount)'; + string internal constant REPAY_PARAMS_DEF = + 'RepayParams(address spoke,uint256 reserveId,uint256 amount)'; + string internal constant SET_USING_AS_COLLATERAL_PARAMS_DEF = + 'SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)'; + string internal constant SUPPLY_PARAMS_DEF = + 'SupplyParams(address spoke,uint256 reserveId,uint256 amount)'; + string internal constant UPDATE_USER_DYNAMIC_CONFIG_PARAMS_DEF = + 'UpdateUserDynamicConfigParams(address spoke)'; + string internal constant UPDATE_USER_RISK_PREMIUM_PARAMS_DEF = + 'UpdateUserRiskPremiumParams(address spoke)'; + string internal constant WITHDRAW_PARAMS_DEF = + 'WithdrawParams(address spoke,uint256 reserveId,uint256 amount)'; + + bytes32 internal constant SUPPLY_PARAMS_TYPEHASH = + // keccak256('SupplyParams(address spoke,uint256 reserveId,uint256 amount)') + 0x1b6c40592fda6c0e86066b14d06a185a879ce67373f4cd91b4fe3e33349bc0e4; + + bytes32 internal constant WITHDRAW_PARAMS_TYPEHASH = + // keccak256('WithdrawParams(address spoke,uint256 reserveId,uint256 amount)') + 0x58e75e9fd311eede04e4a3321d00361fa26e40f92d428b2f3b7112091c0860f2; + + bytes32 internal constant BORROW_PARAMS_TYPEHASH = + // keccak256('BorrowParams(address spoke,uint256 reserveId,uint256 amount)') + 0x27e94de9e568a399cf53391caca93fcca225076a0b9f458c66cd5fa6ee6ceb38; + + bytes32 internal constant REPAY_PARAMS_TYPEHASH = + // keccak256('RepayParams(address spoke,uint256 reserveId,uint256 amount)') + 0x48a24fcb78882348e1291dd601640ee99f1b94a1225439c70518495fe9d1ba0f; + + bytes32 internal constant SET_USING_AS_COLLATERAL_PARAMS_TYPEHASH = + // keccak256('SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)') + 0xbeb8a283b5a625e3baa522c56742f1fd573cf67a4a5f3e8fa864d4baa30901cf; + + bytes32 internal constant UPDATE_USER_RISK_PREMIUM_PARAMS_TYPEHASH = + // keccak256('UpdateUserRiskPremiumParams(address spoke)') + 0xbe8b5422434fc4c91f3a5701cf247efae41407cd51fe5ea72082e9d8d2e5f83d; + + bytes32 internal constant UPDATE_USER_DYNAMIC_CONFIG_PARAMS_TYPEHASH = + // keccak256('UpdateUserDynamicConfigParams(address spoke)') + 0xbf8bd4f7648216d4e812c05faf8b3c04c9d48c630d02d868deb2ad3dc4dd45a5; bytes internal constant ACTION_FIELD_PREFIX = ' action'; bytes internal constant FIELD_SEPARATOR = ','; @@ -76,7 +78,7 @@ library BatchEIP712 { /// @notice Build the full batch type string from an array of action types. /// @dev The type string format is: - /// Batch({ActionType} action0,{ActionType} action1,...,address onBehalfOf,uint256 nonce,uint256 deadline){TypeDefs} + /// Batch({ParamsType} action0,{ParamsType} action1,...,address onBehalfOf,uint256 nonce,uint256 deadline){TypeDefs} /// where {TypeDefs} are the nested type definitions in alphabetical order. /// @param actionTypes Array of action type enum values. /// @return The constructed type string. @@ -93,10 +95,10 @@ library BatchEIP712 { uint8 actionType = actionTypes[i]; usedTypes |= uint8(1 << actionType); - // Append "{ActionTypeName} action{i}," + // Append "{ParamsTypeName} action{i}," batchPart = bytes.concat( batchPart, - bytes(_getActionTypeName(actionType)), + bytes(_getParamsTypeName(actionType)), ACTION_FIELD_PREFIX, bytes(_uintToString(i)), FIELD_SEPARATOR @@ -111,25 +113,25 @@ library BatchEIP712 { bytes memory typeDefs = ''; if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Borrow)) != 0) { - typeDefs = bytes.concat(typeDefs, bytes(BORROW_ACTION_DEF)); + typeDefs = bytes.concat(typeDefs, bytes(BORROW_PARAMS_DEF)); } if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Repay)) != 0) { - typeDefs = bytes.concat(typeDefs, bytes(REPAY_ACTION_DEF)); + typeDefs = bytes.concat(typeDefs, bytes(REPAY_PARAMS_DEF)); } if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) != 0) { - typeDefs = bytes.concat(typeDefs, bytes(SET_USING_AS_COLLATERAL_ACTION_DEF)); + typeDefs = bytes.concat(typeDefs, bytes(SET_USING_AS_COLLATERAL_PARAMS_DEF)); } if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Supply)) != 0) { - typeDefs = bytes.concat(typeDefs, bytes(SUPPLY_ACTION_DEF)); + typeDefs = bytes.concat(typeDefs, bytes(SUPPLY_PARAMS_DEF)); } if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) != 0) { - typeDefs = bytes.concat(typeDefs, bytes(UPDATE_USER_DYNAMIC_CONFIG_ACTION_DEF)); + typeDefs = bytes.concat(typeDefs, bytes(UPDATE_USER_DYNAMIC_CONFIG_PARAMS_DEF)); } if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) != 0) { - typeDefs = bytes.concat(typeDefs, bytes(UPDATE_USER_RISK_PREMIUM_ACTION_DEF)); + typeDefs = bytes.concat(typeDefs, bytes(UPDATE_USER_RISK_PREMIUM_PARAMS_DEF)); } if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Withdraw)) != 0) { - typeDefs = bytes.concat(typeDefs, bytes(WITHDRAW_ACTION_DEF)); + typeDefs = bytes.concat(typeDefs, bytes(WITHDRAW_PARAMS_DEF)); } return string(bytes.concat(batchPart, typeDefs)); @@ -137,71 +139,71 @@ library BatchEIP712 { /// @notice Hash a specific action based on its type. /// @param actionType The action type enum value. - /// @param actionData The ABI-encoded action struct. - /// @return The struct hash of the action. + /// @param actionData The ABI-encoded params struct. + /// @return The struct hash of the params. function hashAction(uint8 actionType, bytes memory actionData) internal pure returns (bytes32) { if (actionType == uint8(ISignatureGateway.ActionType.Supply)) { - ISignatureGateway.SupplyAction memory action = abi.decode( + ISignatureGateway.SupplyParams memory params = abi.decode( actionData, - (ISignatureGateway.SupplyAction) + (ISignatureGateway.SupplyParams) ); return keccak256( - abi.encode(SUPPLY_ACTION_TYPEHASH, action.spoke, action.reserveId, action.amount) + abi.encode(SUPPLY_PARAMS_TYPEHASH, params.spoke, params.reserveId, params.amount) ); } else if (actionType == uint8(ISignatureGateway.ActionType.Withdraw)) { - ISignatureGateway.WithdrawAction memory action = abi.decode( + ISignatureGateway.WithdrawParams memory params = abi.decode( actionData, - (ISignatureGateway.WithdrawAction) + (ISignatureGateway.WithdrawParams) ); return keccak256( - abi.encode(WITHDRAW_ACTION_TYPEHASH, action.spoke, action.reserveId, action.amount) + abi.encode(WITHDRAW_PARAMS_TYPEHASH, params.spoke, params.reserveId, params.amount) ); } else if (actionType == uint8(ISignatureGateway.ActionType.Borrow)) { - ISignatureGateway.BorrowAction memory action = abi.decode( + ISignatureGateway.BorrowParams memory params = abi.decode( actionData, - (ISignatureGateway.BorrowAction) + (ISignatureGateway.BorrowParams) ); return keccak256( - abi.encode(BORROW_ACTION_TYPEHASH, action.spoke, action.reserveId, action.amount) + abi.encode(BORROW_PARAMS_TYPEHASH, params.spoke, params.reserveId, params.amount) ); } else if (actionType == uint8(ISignatureGateway.ActionType.Repay)) { - ISignatureGateway.RepayAction memory action = abi.decode( + ISignatureGateway.RepayParams memory params = abi.decode( actionData, - (ISignatureGateway.RepayAction) + (ISignatureGateway.RepayParams) ); return - keccak256(abi.encode(REPAY_ACTION_TYPEHASH, action.spoke, action.reserveId, action.amount)); + keccak256(abi.encode(REPAY_PARAMS_TYPEHASH, params.spoke, params.reserveId, params.amount)); } else if (actionType == uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) { - ISignatureGateway.SetUsingAsCollateralAction memory action = abi.decode( + ISignatureGateway.SetUsingAsCollateralParams memory params = abi.decode( actionData, - (ISignatureGateway.SetUsingAsCollateralAction) + (ISignatureGateway.SetUsingAsCollateralParams) ); return keccak256( abi.encode( - SET_USING_AS_COLLATERAL_ACTION_TYPEHASH, - action.spoke, - action.reserveId, - action.useAsCollateral + SET_USING_AS_COLLATERAL_PARAMS_TYPEHASH, + params.spoke, + params.reserveId, + params.useAsCollateral ) ); } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) { - ISignatureGateway.UpdateUserRiskPremiumAction memory action = abi.decode( + ISignatureGateway.UpdateUserRiskPremiumParams memory params = abi.decode( actionData, - (ISignatureGateway.UpdateUserRiskPremiumAction) + (ISignatureGateway.UpdateUserRiskPremiumParams) ); - return keccak256(abi.encode(UPDATE_USER_RISK_PREMIUM_ACTION_TYPEHASH, action.spoke)); + return keccak256(abi.encode(UPDATE_USER_RISK_PREMIUM_PARAMS_TYPEHASH, params.spoke)); } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) { - ISignatureGateway.UpdateUserDynamicConfigAction memory action = abi.decode( + ISignatureGateway.UpdateUserDynamicConfigParams memory params = abi.decode( actionData, - (ISignatureGateway.UpdateUserDynamicConfigAction) + (ISignatureGateway.UpdateUserDynamicConfigParams) ); - return keccak256(abi.encode(UPDATE_USER_DYNAMIC_CONFIG_ACTION_TYPEHASH, action.spoke)); + return keccak256(abi.encode(UPDATE_USER_DYNAMIC_CONFIG_PARAMS_TYPEHASH, params.spoke)); } else { - revert('BatchEIP712: invalid action type'); + revert InvalidActionType(actionType); } } @@ -209,7 +211,7 @@ library BatchEIP712 { /// @dev Per EIP-712, struct hash = keccak256(abi.encode(typeHash, member1, member2, ...)) /// For nested structs, each member is the hashStruct of that nested struct. /// @param actionTypes Array of action type enum values. - /// @param actionData Array of ABI-encoded action structs. + /// @param actionData Array of ABI-encoded params structs. /// @param onBehalfOf The user on whose behalf all actions are performed. /// @param nonce The nonce for replay protection. /// @param deadline The deadline for the batch. @@ -238,26 +240,26 @@ library BatchEIP712 { return keccak256(encoded); } - /// @notice Get the action type name string for a given action type. + /// @notice Get the params type name string for a given action type. /// @param actionType The action type enum value. - /// @return The action type name string. - function _getActionTypeName(uint8 actionType) private pure returns (string memory) { + /// @return The params type name string. + function _getParamsTypeName(uint8 actionType) private pure returns (string memory) { if (actionType == uint8(ISignatureGateway.ActionType.Supply)) { - return SUPPLY_ACTION_NAME; + return SUPPLY_PARAMS_NAME; } else if (actionType == uint8(ISignatureGateway.ActionType.Withdraw)) { - return WITHDRAW_ACTION_NAME; + return WITHDRAW_PARAMS_NAME; } else if (actionType == uint8(ISignatureGateway.ActionType.Borrow)) { - return BORROW_ACTION_NAME; + return BORROW_PARAMS_NAME; } else if (actionType == uint8(ISignatureGateway.ActionType.Repay)) { - return REPAY_ACTION_NAME; + return REPAY_PARAMS_NAME; } else if (actionType == uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) { - return SET_USING_AS_COLLATERAL_ACTION_NAME; + return SET_USING_AS_COLLATERAL_PARAMS_NAME; } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) { - return UPDATE_USER_RISK_PREMIUM_ACTION_NAME; + return UPDATE_USER_RISK_PREMIUM_PARAMS_NAME; } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) { - return UPDATE_USER_DYNAMIC_CONFIG_ACTION_NAME; + return UPDATE_USER_DYNAMIC_CONFIG_PARAMS_NAME; } else { - revert('BatchEIP712: invalid action type'); + revert InvalidActionType(actionType); } } diff --git a/src/position-manager/libraries/EIP712Hash.sol b/src/position-manager/libraries/EIP712Hash.sol index 00750cf7f..9716014da 100644 --- a/src/position-manager/libraries/EIP712Hash.sol +++ b/src/position-manager/libraries/EIP712Hash.sol @@ -8,138 +8,206 @@ import {ISignatureGateway} from 'src/position-manager/interfaces/ISignatureGatew /// @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; + // keccak256('SupplyAction(address onBehalfOf,uint256 nonce,uint256 deadline,SupplyParams params)SupplyParams(address spoke,uint256 reserveId,uint256 amount)') + 0xbb32af8a7bebc0600e2174d08c4269abad5d0d78b18faaf394a11dfe08877e05; + + bytes32 public constant SUPPLY_PARAMS_TYPEHASH = + // keccak256('SupplyParams(address spoke,uint256 reserveId,uint256 amount)') + 0x1b6c40592fda6c0e86066b14d06a185a879ce67373f4cd91b4fe3e33349bc0e4; bytes32 public constant WITHDRAW_TYPEHASH = - // keccak256('Withdraw(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)') - 0x0bc73eb58cf4068a29b9593ef18c0d26b3b4453bd2155424a90cb26a22f41d7f; + // keccak256('WithdrawAction(address onBehalfOf,uint256 nonce,uint256 deadline,WithdrawParams params)WithdrawParams(address spoke,uint256 reserveId,uint256 amount)') + 0x970c03fef0ce23693f4d291532e982d4036fc1c297c8e6eb5eb8cc9e8a43c18b; + + bytes32 public constant WITHDRAW_PARAMS_TYPEHASH = + // keccak256('WithdrawParams(address spoke,uint256 reserveId,uint256 amount)') + 0x58e75e9fd311eede04e4a3321d00361fa26e40f92d428b2f3b7112091c0860f2; bytes32 public constant BORROW_TYPEHASH = - // keccak256('Borrow(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)') - 0xe248895a233688ba2a70b6f560472dbc27e35ece0d86914f7d43bf2f7df8025b; + // keccak256('BorrowAction(address onBehalfOf,uint256 nonce,uint256 deadline,BorrowParams params)BorrowParams(address spoke,uint256 reserveId,uint256 amount)') + 0xdf9c4985a627692f0f61e1ff6443dd1c52c4838ad331885a379eda2b5936bbd7; + + bytes32 public constant BORROW_PARAMS_TYPEHASH = + // keccak256('BorrowParams(address spoke,uint256 reserveId,uint256 amount)') + 0x27e94de9e568a399cf53391caca93fcca225076a0b9f458c66cd5fa6ee6ceb38; bytes32 public constant REPAY_TYPEHASH = - // keccak256('Repay(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)') - 0xd23fe99a7aac398d03952a098faa8889259d062784bd80ea0f159e4af604c045; + // keccak256('RepayAction(address onBehalfOf,uint256 nonce,uint256 deadline,RepayParams params)RepayParams(address spoke,uint256 reserveId,uint256 amount)') + 0x39155913bb8fdc35112317080698e2c7eb9c24b0e32ce09f895fa123280c3aa4; + + bytes32 public constant REPAY_PARAMS_TYPEHASH = + // keccak256('RepayParams(address spoke,uint256 reserveId,uint256 amount)') + 0x48a24fcb78882348e1291dd601640ee99f1b94a1225439c70518495fe9d1ba0f; bytes32 public constant SET_USING_AS_COLLATERAL_TYPEHASH = - // keccak256('SetUsingAsCollateral(address spoke,uint256 reserveId,bool useAsCollateral,address onBehalfOf,uint256 nonce,uint256 deadline)') - 0xd4350e1f25ecd62a35b50e8cd1e00bc34331ae8c728ee4dbb69ecf1023daecf7; + // keccak256('SetUsingAsCollateralAction(address onBehalfOf,uint256 nonce,uint256 deadline,SetUsingAsCollateralParams params)SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)') + 0x5c7cf8b2daccf16abc5c02b884ab19466a70d693b4831cfc8bb5cfccdda53466; + + bytes32 public constant SET_USING_AS_COLLATERAL_PARAMS_TYPEHASH = + // keccak256('SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)') + 0xbeb8a283b5a625e3baa522c56742f1fd573cf67a4a5f3e8fa864d4baa30901cf; bytes32 public constant UPDATE_USER_RISK_PREMIUM_TYPEHASH = - // keccak256('UpdateUserRiskPremium(address spoke,address user,uint256 nonce,uint256 deadline)') - 0xb41e132023782c9b02febf1b9b7fe98c4a73f57ebc63ba44cd71f6365ea09eaf; + // keccak256('UpdateUserRiskPremiumAction(address user,uint256 nonce,uint256 deadline,UpdateUserRiskPremiumParams params)UpdateUserRiskPremiumParams(address spoke)') + 0x06f48accbacba38999087c808cd2206162256b5e2a1656388bbf2045ad38c55a; + + bytes32 public constant UPDATE_USER_RISK_PREMIUM_PARAMS_TYPEHASH = + // keccak256('UpdateUserRiskPremiumParams(address spoke)') + 0xbe8b5422434fc4c91f3a5701cf247efae41407cd51fe5ea72082e9d8d2e5f83d; bytes32 public constant UPDATE_USER_DYNAMIC_CONFIG_TYPEHASH = - // keccak256('UpdateUserDynamicConfig(address spoke,address user,uint256 nonce,uint256 deadline)') - 0xba177b1f5b5e1e709f62c19f03c97988c57752ba561de58f383ebee4e8d0a71c; + // keccak256('UpdateUserDynamicConfigAction(address user,uint256 nonce,uint256 deadline,UpdateUserDynamicConfigParams params)UpdateUserDynamicConfigParams(address spoke)') + 0x8b3e676fb7c7341a0fb375c2cc2f84f9a35c8090ffcc7262da10544389a08731; - function hash(ISignatureGateway.Supply calldata params) internal pure returns (bytes32) { + bytes32 public constant UPDATE_USER_DYNAMIC_CONFIG_PARAMS_TYPEHASH = + // keccak256('UpdateUserDynamicConfigParams(address spoke)') + 0xbf8bd4f7648216d4e812c05faf8b3c04c9d48c630d02d868deb2ad3dc4dd45a5; + + function hash(ISignatureGateway.SupplyAction calldata action) internal pure returns (bytes32) { return keccak256( abi.encode( SUPPLY_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline + action.onBehalfOf, + action.nonce, + action.deadline, + action.params.hash() ) ); } - function hash(ISignatureGateway.Withdraw calldata params) internal pure returns (bytes32) { + function hash(ISignatureGateway.SupplyParams calldata params) internal pure returns (bytes32) { + return + keccak256(abi.encode(SUPPLY_PARAMS_TYPEHASH, params.spoke, params.reserveId, params.amount)); + } + + function hash(ISignatureGateway.WithdrawAction calldata action) internal pure returns (bytes32) { return keccak256( abi.encode( WITHDRAW_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline + action.onBehalfOf, + action.nonce, + action.deadline, + action.params.hash() ) ); } - function hash(ISignatureGateway.Borrow calldata params) internal pure returns (bytes32) { + function hash(ISignatureGateway.WithdrawParams calldata params) internal pure returns (bytes32) { + return + keccak256( + abi.encode(WITHDRAW_PARAMS_TYPEHASH, params.spoke, params.reserveId, params.amount) + ); + } + + function hash(ISignatureGateway.BorrowAction calldata action) internal pure returns (bytes32) { return keccak256( abi.encode( BORROW_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline + action.onBehalfOf, + action.nonce, + action.deadline, + action.params.hash() ) ); } - function hash(ISignatureGateway.Repay calldata params) internal pure returns (bytes32) { + function hash(ISignatureGateway.BorrowParams calldata params) internal pure returns (bytes32) { + return + keccak256(abi.encode(BORROW_PARAMS_TYPEHASH, params.spoke, params.reserveId, params.amount)); + } + + function hash(ISignatureGateway.RepayAction calldata action) internal pure returns (bytes32) { return keccak256( abi.encode( REPAY_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline + action.onBehalfOf, + action.nonce, + action.deadline, + action.params.hash() ) ); } + function hash(ISignatureGateway.RepayParams calldata params) internal pure returns (bytes32) { + return + keccak256(abi.encode(REPAY_PARAMS_TYPEHASH, params.spoke, params.reserveId, params.amount)); + } + function hash( - ISignatureGateway.SetUsingAsCollateral calldata params + ISignatureGateway.SetUsingAsCollateralAction calldata action ) internal pure returns (bytes32) { return keccak256( abi.encode( SET_USING_AS_COLLATERAL_TYPEHASH, + action.onBehalfOf, + action.nonce, + action.deadline, + action.params.hash() + ) + ); + } + + function hash( + ISignatureGateway.SetUsingAsCollateralParams calldata params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + SET_USING_AS_COLLATERAL_PARAMS_TYPEHASH, params.spoke, params.reserveId, - params.useAsCollateral, - params.onBehalfOf, - params.nonce, - params.deadline + params.useAsCollateral ) ); } function hash( - ISignatureGateway.UpdateUserRiskPremium calldata params + ISignatureGateway.UpdateUserRiskPremiumAction calldata action ) internal pure returns (bytes32) { return keccak256( abi.encode( UPDATE_USER_RISK_PREMIUM_TYPEHASH, - params.spoke, - params.user, - params.nonce, - params.deadline + action.user, + action.nonce, + action.deadline, + action.params.hash() ) ); } function hash( - ISignatureGateway.UpdateUserDynamicConfig calldata params + ISignatureGateway.UpdateUserRiskPremiumParams calldata params + ) internal pure returns (bytes32) { + return keccak256(abi.encode(UPDATE_USER_RISK_PREMIUM_PARAMS_TYPEHASH, params.spoke)); + } + + function hash( + ISignatureGateway.UpdateUserDynamicConfigAction calldata action ) internal pure returns (bytes32) { return keccak256( abi.encode( UPDATE_USER_DYNAMIC_CONFIG_TYPEHASH, - params.spoke, - params.user, - params.nonce, - params.deadline + action.user, + action.nonce, + action.deadline, + action.params.hash() ) ); } + + function hash( + ISignatureGateway.UpdateUserDynamicConfigParams calldata params + ) internal pure returns (bytes32) { + return keccak256(abi.encode(UPDATE_USER_DYNAMIC_CONFIG_PARAMS_TYPEHASH, params.spoke)); + } } diff --git a/tests/gas/Gateways.Operations.gas.t.sol b/tests/gas/Gateways.Operations.gas.t.sol index bb277fff5..0bd855aed 100644 --- a/tests/gas/Gateways.Operations.gas.t.sol +++ b/tests/gas/Gateways.Operations.gas.t.sol @@ -117,121 +117,143 @@ contract SignatureGateway_Gas_Tests is SignatureGatewayBaseTest { } function test_supplyWithSig() public { - ISignatureGateway.Supply memory p = ISignatureGateway.Supply({ - spoke: address(spoke1), - reserveId: _wethReserveId(spoke1), - amount: 100e18, + uint256 reserveId = _wethReserveId(spoke1); + uint256 amount = 100e18; + ISignatureGateway.SupplyAction memory action = ISignatureGateway.SupplyAction({ onBehalfOf: alice, nonce: gateway.nonces(alice, nonceKey), - deadline: _warpBeforeRandomDeadline() + deadline: _warpBeforeRandomDeadline(), + params: ISignatureGateway.SupplyParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount + }) }); - bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - Utils.approve(spoke1, p.reserveId, alice, address(gateway), p.amount); - Utils.supply(spoke1, p.reserveId, alice, p.amount, alice); + bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, action)); + Utils.approve(spoke1, reserveId, alice, address(gateway), amount); + Utils.supply(spoke1, reserveId, alice, amount, alice); - gateway.supplyWithSig(p, signature); + gateway.supplyWithSig(action, signature); vm.snapshotGasLastCall(NAMESPACE, 'supplyWithSig'); } function test_withdrawWithSig() public { - ISignatureGateway.Withdraw memory p = ISignatureGateway.Withdraw({ - spoke: address(spoke1), - reserveId: _wethReserveId(spoke1), - amount: 100e18, + uint256 reserveId = _wethReserveId(spoke1); + uint256 amount = 100e18; + ISignatureGateway.WithdrawAction memory action = ISignatureGateway.WithdrawAction({ onBehalfOf: alice, nonce: gateway.nonces(alice, nonceKey), - deadline: _warpBeforeRandomDeadline() + deadline: _warpBeforeRandomDeadline(), + params: ISignatureGateway.WithdrawParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount + }) }); - bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); + bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, action)); - Utils.supply(spoke1, p.reserveId, alice, 200e18, alice); - Utils.withdraw(spoke1, p.reserveId, alice, 100e18, alice); + Utils.supply(spoke1, reserveId, alice, 200e18, alice); + Utils.withdraw(spoke1, reserveId, alice, 100e18, alice); - gateway.withdrawWithSig(p, signature); + gateway.withdrawWithSig(action, signature); vm.snapshotGasLastCall(NAMESPACE, 'withdrawWithSig'); } function test_borrowWithSig() public { - ISignatureGateway.Borrow memory p = ISignatureGateway.Borrow({ - spoke: address(spoke1), - reserveId: _wethReserveId(spoke1), - amount: 100e18, + uint256 reserveId = _wethReserveId(spoke1); + uint256 amount = 100e18; + ISignatureGateway.BorrowAction memory action = ISignatureGateway.BorrowAction({ onBehalfOf: alice, nonce: gateway.nonces(alice, nonceKey), - deadline: _warpBeforeRandomDeadline() + deadline: _warpBeforeRandomDeadline(), + params: ISignatureGateway.BorrowParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount + }) }); - Utils.supplyCollateral(spoke1, p.reserveId, alice, p.amount * 4, alice); - Utils.borrow(spoke1, p.reserveId, alice, p.amount, alice); - bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); + Utils.supplyCollateral(spoke1, reserveId, alice, amount * 4, alice); + Utils.borrow(spoke1, reserveId, alice, amount, alice); + bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, action)); - gateway.borrowWithSig(p, signature); + gateway.borrowWithSig(action, signature); vm.snapshotGasLastCall(NAMESPACE, 'borrowWithSig'); } function test_repayWithSig() public { - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: _wethReserveId(spoke1), - amount: 100e18, + uint256 reserveId = _wethReserveId(spoke1); + uint256 amount = 100e18; + ISignatureGateway.RepayAction memory action = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: gateway.nonces(alice, nonceKey), - deadline: _warpBeforeRandomDeadline() + deadline: _warpBeforeRandomDeadline(), + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount + }) }); - Utils.supplyCollateral(spoke1, p.reserveId, alice, p.amount * 10, alice); - Utils.borrow(spoke1, p.reserveId, alice, p.amount * 3, alice); - Utils.approve(spoke1, p.reserveId, alice, address(gateway), p.amount * 2); - Utils.repay(spoke1, p.reserveId, alice, p.amount, alice); - bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); + Utils.supplyCollateral(spoke1, reserveId, alice, amount * 10, alice); + Utils.borrow(spoke1, reserveId, alice, amount * 3, alice); + Utils.approve(spoke1, reserveId, alice, address(gateway), amount * 2); + Utils.repay(spoke1, reserveId, alice, amount, alice); + bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, action)); - gateway.repayWithSig(p, signature); + gateway.repayWithSig(action, signature); vm.snapshotGasLastCall(NAMESPACE, 'repayWithSig'); } function test_setUsingAsCollateralWithSig() public { - ISignatureGateway.SetUsingAsCollateral memory p = ISignatureGateway.SetUsingAsCollateral({ - spoke: address(spoke1), - reserveId: _wethReserveId(spoke1), - useAsCollateral: true, - onBehalfOf: alice, - nonce: gateway.nonces(alice, nonceKey), - deadline: _warpBeforeRandomDeadline() - }); - Utils.supply(spoke1, p.reserveId, alice, 1e18, alice); - bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - - gateway.setUsingAsCollateralWithSig(p, signature); + uint256 reserveId = _wethReserveId(spoke1); + ISignatureGateway.SetUsingAsCollateralAction memory action = ISignatureGateway + .SetUsingAsCollateralAction({ + onBehalfOf: alice, + nonce: gateway.nonces(alice, nonceKey), + deadline: _warpBeforeRandomDeadline(), + params: ISignatureGateway.SetUsingAsCollateralParams({ + spoke: address(spoke1), + reserveId: reserveId, + useAsCollateral: true + }) + }); + Utils.supply(spoke1, reserveId, alice, 1e18, alice); + bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, action)); + + gateway.setUsingAsCollateralWithSig(action, signature); vm.snapshotGasLastCall(NAMESPACE, 'setUsingAsCollateralWithSig'); } function test_updateUserRiskPremiumWithSig() public { - ISignatureGateway.UpdateUserRiskPremium memory p = ISignatureGateway.UpdateUserRiskPremium({ - spoke: address(spoke1), - user: alice, - nonce: gateway.nonces(alice, nonceKey), - deadline: _warpBeforeRandomDeadline() - }); - bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); + ISignatureGateway.UpdateUserRiskPremiumAction memory action = ISignatureGateway + .UpdateUserRiskPremiumAction({ + user: alice, + nonce: gateway.nonces(alice, nonceKey), + deadline: _warpBeforeRandomDeadline(), + params: ISignatureGateway.UpdateUserRiskPremiumParams({spoke: address(spoke1)}) + }); + bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, action)); vm.prank(alice); spoke1.updateUserRiskPremium(alice); - gateway.updateUserRiskPremiumWithSig(p, signature); + gateway.updateUserRiskPremiumWithSig(action, signature); vm.snapshotGasLastCall(NAMESPACE, 'updateUserRiskPremiumWithSig'); } function test_updateUserDynamicConfigWithSig() public { - ISignatureGateway.UpdateUserDynamicConfig memory p = ISignatureGateway.UpdateUserDynamicConfig({ - spoke: address(spoke1), - user: alice, - nonce: gateway.nonces(alice, nonceKey), - deadline: _warpBeforeRandomDeadline() - }); - bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); + ISignatureGateway.UpdateUserDynamicConfigAction memory action = ISignatureGateway + .UpdateUserDynamicConfigAction({ + user: alice, + nonce: gateway.nonces(alice, nonceKey), + deadline: _warpBeforeRandomDeadline(), + params: ISignatureGateway.UpdateUserDynamicConfigParams({spoke: address(spoke1)}) + }); + bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, action)); vm.prank(alice); spoke1.updateUserDynamicConfig(alice); - gateway.updateUserDynamicConfigWithSig(p, signature); + gateway.updateUserDynamicConfigWithSig(action, signature); vm.snapshotGasLastCall(NAMESPACE, 'updateUserDynamicConfigWithSig'); } @@ -308,7 +330,7 @@ contract SignatureGatewayBatch_Gas_Tests is SignatureGatewayBaseTest { bytes[] memory actionData = new bytes[](1); actionData[0] = abi.encode( - ISignatureGateway.SupplyAction({spoke: address(spoke1), reserveId: reserveId, amount: amount}) + ISignatureGateway.SupplyParams({spoke: address(spoke1), reserveId: reserveId, amount: amount}) ); bytes memory signature = _sign( @@ -334,10 +356,10 @@ contract SignatureGatewayBatch_Gas_Tests is SignatureGatewayBaseTest { bytes[] memory actionData = new bytes[](2); actionData[0] = abi.encode( - ISignatureGateway.SupplyAction({spoke: address(spoke1), reserveId: reserveId, amount: amount}) + ISignatureGateway.SupplyParams({spoke: address(spoke1), reserveId: reserveId, amount: amount}) ); actionData[1] = abi.encode( - ISignatureGateway.SetUsingAsCollateralAction({ + ISignatureGateway.SetUsingAsCollateralParams({ spoke: address(spoke1), reserveId: reserveId, useAsCollateral: true @@ -370,14 +392,14 @@ contract SignatureGatewayBatch_Gas_Tests is SignatureGatewayBaseTest { bytes[] memory actionData = new bytes[](2); actionData[0] = abi.encode( - ISignatureGateway.SupplyAction({ + ISignatureGateway.SupplyParams({ spoke: address(spoke1), reserveId: reserveId, amount: supplyAmount }) ); actionData[1] = abi.encode( - ISignatureGateway.WithdrawAction({ + ISignatureGateway.WithdrawParams({ spoke: address(spoke1), reserveId: reserveId, amount: withdrawAmount @@ -411,14 +433,14 @@ contract SignatureGatewayBatch_Gas_Tests is SignatureGatewayBaseTest { bytes[] memory actionData = new bytes[](2); actionData[0] = abi.encode( - ISignatureGateway.BorrowAction({ + ISignatureGateway.BorrowParams({ spoke: address(spoke1), reserveId: reserveId, amount: borrowAmount }) ); actionData[1] = abi.encode( - ISignatureGateway.RepayAction({ + ISignatureGateway.RepayParams({ spoke: address(spoke1), reserveId: reserveId, amount: repayAmount @@ -451,17 +473,17 @@ contract SignatureGatewayBatch_Gas_Tests is SignatureGatewayBaseTest { bytes[] memory actionData = new bytes[](3); actionData[0] = abi.encode( - ISignatureGateway.SupplyAction({spoke: address(spoke1), reserveId: reserveId, amount: amount}) + ISignatureGateway.SupplyParams({spoke: address(spoke1), reserveId: reserveId, amount: amount}) ); actionData[1] = abi.encode( - ISignatureGateway.SetUsingAsCollateralAction({ + ISignatureGateway.SetUsingAsCollateralParams({ spoke: address(spoke1), reserveId: reserveId, useAsCollateral: true }) ); actionData[2] = abi.encode( - ISignatureGateway.WithdrawAction({ + ISignatureGateway.WithdrawParams({ spoke: address(spoke1), reserveId: reserveId, amount: amount / 2 @@ -501,31 +523,31 @@ contract SignatureGatewayBatch_Gas_Tests is SignatureGatewayBaseTest { bytes[] memory actionData = new bytes[](5); actionData[0] = abi.encode( - ISignatureGateway.SupplyAction({spoke: address(spoke1), reserveId: reserveId, amount: amount}) + ISignatureGateway.SupplyParams({spoke: address(spoke1), reserveId: reserveId, amount: amount}) ); actionData[1] = abi.encode( - ISignatureGateway.SetUsingAsCollateralAction({ + ISignatureGateway.SetUsingAsCollateralParams({ spoke: address(spoke1), reserveId: reserveId, useAsCollateral: true }) ); actionData[2] = abi.encode( - ISignatureGateway.BorrowAction({ + ISignatureGateway.BorrowParams({ spoke: address(spoke1), reserveId: reserveId, amount: amount / 2 }) ); actionData[3] = abi.encode( - ISignatureGateway.RepayAction({ + ISignatureGateway.RepayParams({ spoke: address(spoke1), reserveId: reserveId, amount: amount / 4 }) ); actionData[4] = abi.encode( - ISignatureGateway.WithdrawAction({ + ISignatureGateway.WithdrawParams({ spoke: address(spoke1), reserveId: reserveId, amount: amount / 4 @@ -562,70 +584,70 @@ contract SignatureGatewayPermit2_Gas_Tests is SignatureGatewayPermit2BaseTest { uint256 deadline = vm.getBlockTimestamp() + 1 hours; uint256 gatewayNonce = gateway.nonces(alice, 100); + ISignatureGateway.SupplyAction memory action = ISignatureGateway.SupplyAction({ + onBehalfOf: alice, + nonce: gatewayNonce, + deadline: deadline, + params: ISignatureGateway.SupplyParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount + }) + }); + ISignatureTransfer.PermitTransferFrom memory permit = ISignatureTransfer.PermitTransferFrom({ permitted: ISignatureTransfer.TokenPermissions({ token: address(_underlying(spoke1, reserveId)), amount: amount }), - nonce: 0, - deadline: deadline - }); - - ISignatureGateway.Supply memory p = ISignatureGateway.Supply({ - spoke: address(spoke1), - reserveId: reserveId, - amount: amount, - onBehalfOf: alice, - nonce: gatewayNonce, + nonce: _randomUnusedPermit2Nonce(alice), deadline: deadline }); - bytes memory signature = _getPermit2SupplySignature(permit, p, alicePk); + bytes memory signature = _getPermit2SupplySignature(permit, action, alicePk); _approvePermit2(spoke1, reserveId, alice); Utils.supply(spoke1, reserveId, alice, amount, alice); - deal(permit.permitted.token, alice, amount); - gateway.supplyWithPermit2(permit, p, signature); + gateway.supplyWithPermit2(permit, action, signature); vm.snapshotGasLastCall(NAMESPACE, 'supplyWithPermit2'); } function test_repayWithPermit2() public { uint256 reserveId = _wethReserveId(spoke1); - uint256 supplyAmount = 1000e18; - uint256 borrowAmount = 300e18; - uint256 repayAmount = 100e18; + uint256 amount = 100e18; uint256 deadline = vm.getBlockTimestamp() + 1 hours; + uint256 gatewayNonce = gateway.nonces(alice, 100); - Utils.supplyCollateral(spoke1, reserveId, alice, supplyAmount, alice); - Utils.borrow(spoke1, reserveId, alice, borrowAmount, alice); - Utils.repay(spoke1, reserveId, alice, repayAmount, alice); + Utils.supplyCollateral(spoke1, reserveId, alice, amount * 10, alice); + Utils.borrow(spoke1, reserveId, alice, amount * 3, alice); + Utils.repay(spoke1, reserveId, alice, amount, alice); - uint256 gatewayNonce = gateway.nonces(alice, 100); + ISignatureGateway.RepayAction memory action = ISignatureGateway.RepayAction({ + onBehalfOf: alice, + nonce: gatewayNonce, + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount + }) + }); ISignatureTransfer.PermitTransferFrom memory permit = ISignatureTransfer.PermitTransferFrom({ permitted: ISignatureTransfer.TokenPermissions({ token: address(_underlying(spoke1, reserveId)), - amount: repayAmount + amount: amount }), - nonce: 1, - deadline: deadline - }); - - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: repayAmount, - onBehalfOf: alice, - nonce: gatewayNonce, + nonce: _randomUnusedPermit2Nonce(alice), deadline: deadline }); - bytes memory signature = _getPermit2RepaySignature(permit, p, alicePk); + bytes memory signature = _getPermit2RepaySignature(permit, action, alicePk); _approvePermit2(spoke1, reserveId, alice); - gateway.repayWithPermit2(permit, p, signature); + gateway.repayWithPermit2(permit, action, signature); vm.snapshotGasLastCall(NAMESPACE, 'repayWithPermit2'); } } diff --git a/tests/mocks/EIP712Types.sol b/tests/mocks/EIP712Types.sol index 21b4e5542..685bc463b 100644 --- a/tests/mocks/EIP712Types.sol +++ b/tests/mocks/EIP712Types.sol @@ -28,63 +28,114 @@ library EIP712Types { uint256 deadline; } - /// @dev SignatureGateway Intents - struct Supply { + /// @dev SignatureGateway Params (nested in Actions) + struct SupplyParams { address spoke; uint256 reserveId; uint256 amount; - address onBehalfOf; - uint256 nonce; - uint256 deadline; } - struct Withdraw { + struct WithdrawParams { address spoke; uint256 reserveId; uint256 amount; - address onBehalfOf; - uint256 nonce; - uint256 deadline; } - struct Borrow { + struct BorrowParams { address spoke; uint256 reserveId; uint256 amount; - address onBehalfOf; - uint256 nonce; - uint256 deadline; } - struct Repay { + struct RepayParams { address spoke; uint256 reserveId; uint256 amount; - address onBehalfOf; - uint256 nonce; - uint256 deadline; } - struct SetUsingAsCollateral { + struct SetUsingAsCollateralParams { address spoke; uint256 reserveId; bool useAsCollateral; + } + + struct UpdateUserRiskPremiumParams { + address spoke; + } + + struct UpdateUserDynamicConfigParams { + address spoke; + } + + /// @dev SignatureGateway Actions (contain nested Params) + struct SupplyAction { address onBehalfOf; uint256 nonce; uint256 deadline; + SupplyParams params; } - struct UpdateUserRiskPremium { - address spoke; + struct WithdrawAction { + address onBehalfOf; + uint256 nonce; + uint256 deadline; + WithdrawParams params; + } + + struct BorrowAction { + address onBehalfOf; + uint256 nonce; + uint256 deadline; + BorrowParams params; + } + + struct RepayAction { + address onBehalfOf; + uint256 nonce; + uint256 deadline; + RepayParams params; + } + + struct SetUsingAsCollateralAction { + address onBehalfOf; + uint256 nonce; + uint256 deadline; + SetUsingAsCollateralParams params; + } + + struct UpdateUserRiskPremiumAction { address user; uint256 nonce; uint256 deadline; + UpdateUserRiskPremiumParams params; } - struct UpdateUserDynamicConfig { - address spoke; + struct UpdateUserDynamicConfigAction { address user; uint256 nonce; uint256 deadline; + UpdateUserDynamicConfigParams params; + } + + /// @dev Permit2 types for witness transfer + struct TokenPermissions { + address token; + uint256 amount; + } + + struct PermitWitnessTransferFromSupplyAction { + TokenPermissions permitted; + address spender; + uint256 nonce; + uint256 deadline; + SupplyAction witness; + } + + struct PermitWitnessTransferFromRepayAction { + TokenPermissions permitted; + address spender; + uint256 nonce; + uint256 deadline; + RepayAction witness; } } diff --git a/tests/mocks/JsonBindings.sol b/tests/mocks/JsonBindings.sol index 0186f03c4..84199aaf4 100644 --- a/tests/mocks/JsonBindings.sol +++ b/tests/mocks/JsonBindings.sol @@ -44,20 +44,46 @@ library JsonBindings { 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)"; + + // Params schemas + // prettier-ignore + string constant schema_SupplyParams = "SupplyParams(address spoke,uint256 reserveId,uint256 amount)"; + // prettier-ignore + string constant schema_WithdrawParams = "WithdrawParams(address spoke,uint256 reserveId,uint256 amount)"; + // prettier-ignore + string constant schema_BorrowParams = "BorrowParams(address spoke,uint256 reserveId,uint256 amount)"; + // prettier-ignore + string constant schema_RepayParams = "RepayParams(address spoke,uint256 reserveId,uint256 amount)"; + // prettier-ignore + string constant schema_SetUsingAsCollateralParams = "SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)"; + // prettier-ignore + string constant schema_UpdateUserRiskPremiumParams = "UpdateUserRiskPremiumParams(address spoke)"; + // prettier-ignore + string constant schema_UpdateUserDynamicConfigParams = "UpdateUserDynamicConfigParams(address spoke)"; + + // Action schemas (with nested Params) + // prettier-ignore + string constant schema_SupplyAction = "SupplyAction(address onBehalfOf,uint256 nonce,uint256 deadline,SupplyParams params)SupplyParams(address spoke,uint256 reserveId,uint256 amount)"; // prettier-ignore - string constant schema_Supply = "Supply(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)"; + string constant schema_WithdrawAction = "WithdrawAction(address onBehalfOf,uint256 nonce,uint256 deadline,WithdrawParams params)WithdrawParams(address spoke,uint256 reserveId,uint256 amount)"; // prettier-ignore - string constant schema_Withdraw = "Withdraw(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)"; + string constant schema_BorrowAction = "BorrowAction(address onBehalfOf,uint256 nonce,uint256 deadline,BorrowParams params)BorrowParams(address spoke,uint256 reserveId,uint256 amount)"; // prettier-ignore - string constant schema_Borrow = "Borrow(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)"; + string constant schema_RepayAction = "RepayAction(address onBehalfOf,uint256 nonce,uint256 deadline,RepayParams params)RepayParams(address spoke,uint256 reserveId,uint256 amount)"; // prettier-ignore - string constant schema_Repay = "Repay(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)"; + string constant schema_SetUsingAsCollateralAction = "SetUsingAsCollateralAction(address onBehalfOf,uint256 nonce,uint256 deadline,SetUsingAsCollateralParams params)SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)"; // prettier-ignore - string constant schema_SetUsingAsCollateral = "SetUsingAsCollateral(address spoke,uint256 reserveId,bool useAsCollateral,address onBehalfOf,uint256 nonce,uint256 deadline)"; + string constant schema_UpdateUserRiskPremiumAction = "UpdateUserRiskPremiumAction(address user,uint256 nonce,uint256 deadline,UpdateUserRiskPremiumParams params)UpdateUserRiskPremiumParams(address spoke)"; // prettier-ignore - string constant schema_UpdateUserRiskPremium = "UpdateUserRiskPremium(address spoke,address user,uint256 nonce,uint256 deadline)"; + string constant schema_UpdateUserDynamicConfigAction = "UpdateUserDynamicConfigAction(address user,uint256 nonce,uint256 deadline,UpdateUserDynamicConfigParams params)UpdateUserDynamicConfigParams(address spoke)"; + + // Permit2 schemas + // prettier-ignore + string constant schema_TokenPermissions = "TokenPermissions(address token,uint256 amount)"; + // prettier-ignore + string constant schema_PermitWitnessTransferFromSupplyAction = "PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,SupplyAction witness)SupplyAction(address onBehalfOf,uint256 nonce,uint256 deadline,SupplyParams params)SupplyParams(address spoke,uint256 reserveId,uint256 amount)TokenPermissions(address token,uint256 amount)"; // prettier-ignore - string constant schema_UpdateUserDynamicConfig = "UpdateUserDynamicConfig(address spoke,address user,uint256 nonce,uint256 deadline)"; + string constant schema_PermitWitnessTransferFromRepayAction = "PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,RepayAction witness)RepayAction(address onBehalfOf,uint256 nonce,uint256 deadline,RepayParams params)RepayParams(address spoke,uint256 reserveId,uint256 amount)TokenPermissions(address token,uint256 amount)"; function serialize( EIP712Types.SetUserPositionManagers memory value @@ -136,266 +162,746 @@ library JsonBindings { return abi.decode(vm.parseJsonTypeArray(json, path, schema_Permit), (EIP712Types.Permit[])); } - function serialize(EIP712Types.Supply memory value) internal pure returns (string memory) { - return vm.serializeJsonType(schema_Supply, abi.encode(value)); + // ============ Params Bindings ============ + + function serialize(EIP712Types.SupplyParams memory value) internal pure returns (string memory) { + return vm.serializeJsonType(schema_SupplyParams, abi.encode(value)); + } + + function serialize( + EIP712Types.SupplyParams memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_SupplyParams, abi.encode(value)); + } + + function deserializeSupplyParams( + string memory json + ) public pure returns (EIP712Types.SupplyParams memory) { + return abi.decode(vm.parseJsonType(json, schema_SupplyParams), (EIP712Types.SupplyParams)); + } + + function deserializeSupplyParams( + string memory json, + string memory path + ) public pure returns (EIP712Types.SupplyParams memory) { + return + abi.decode(vm.parseJsonType(json, path, schema_SupplyParams), (EIP712Types.SupplyParams)); + } + + function deserializeSupplyParamsArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.SupplyParams[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_SupplyParams), + (EIP712Types.SupplyParams[]) + ); + } + + function serialize( + EIP712Types.WithdrawParams memory value + ) internal pure returns (string memory) { + return vm.serializeJsonType(schema_WithdrawParams, abi.encode(value)); + } + + function serialize( + EIP712Types.WithdrawParams memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_WithdrawParams, abi.encode(value)); + } + + function deserializeWithdrawParams( + string memory json + ) public pure returns (EIP712Types.WithdrawParams memory) { + return abi.decode(vm.parseJsonType(json, schema_WithdrawParams), (EIP712Types.WithdrawParams)); + } + + function deserializeWithdrawParams( + string memory json, + string memory path + ) public pure returns (EIP712Types.WithdrawParams memory) { + return + abi.decode(vm.parseJsonType(json, path, schema_WithdrawParams), (EIP712Types.WithdrawParams)); + } + + function deserializeWithdrawParamsArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.WithdrawParams[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_WithdrawParams), + (EIP712Types.WithdrawParams[]) + ); + } + + function serialize(EIP712Types.BorrowParams memory value) internal pure returns (string memory) { + return vm.serializeJsonType(schema_BorrowParams, abi.encode(value)); + } + + function serialize( + EIP712Types.BorrowParams memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_BorrowParams, abi.encode(value)); + } + + function deserializeBorrowParams( + string memory json + ) public pure returns (EIP712Types.BorrowParams memory) { + return abi.decode(vm.parseJsonType(json, schema_BorrowParams), (EIP712Types.BorrowParams)); + } + + function deserializeBorrowParams( + string memory json, + string memory path + ) public pure returns (EIP712Types.BorrowParams memory) { + return + abi.decode(vm.parseJsonType(json, path, schema_BorrowParams), (EIP712Types.BorrowParams)); + } + + function deserializeBorrowParamsArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.BorrowParams[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_BorrowParams), + (EIP712Types.BorrowParams[]) + ); + } + + function serialize(EIP712Types.RepayParams memory value) internal pure returns (string memory) { + return vm.serializeJsonType(schema_RepayParams, abi.encode(value)); + } + + function serialize( + EIP712Types.RepayParams memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_RepayParams, abi.encode(value)); + } + + function deserializeRepayParams( + string memory json + ) public pure returns (EIP712Types.RepayParams memory) { + return abi.decode(vm.parseJsonType(json, schema_RepayParams), (EIP712Types.RepayParams)); + } + + function deserializeRepayParams( + string memory json, + string memory path + ) public pure returns (EIP712Types.RepayParams memory) { + return abi.decode(vm.parseJsonType(json, path, schema_RepayParams), (EIP712Types.RepayParams)); + } + + function deserializeRepayParamsArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.RepayParams[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_RepayParams), + (EIP712Types.RepayParams[]) + ); + } + + function serialize( + EIP712Types.SetUsingAsCollateralParams memory value + ) internal pure returns (string memory) { + return vm.serializeJsonType(schema_SetUsingAsCollateralParams, abi.encode(value)); + } + + function serialize( + EIP712Types.SetUsingAsCollateralParams memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return + vm.serializeJsonType(objectKey, valueKey, schema_SetUsingAsCollateralParams, abi.encode(value)); + } + + function deserializeSetUsingAsCollateralParams( + string memory json + ) public pure returns (EIP712Types.SetUsingAsCollateralParams memory) { + return + abi.decode( + vm.parseJsonType(json, schema_SetUsingAsCollateralParams), + (EIP712Types.SetUsingAsCollateralParams) + ); + } + + function deserializeSetUsingAsCollateralParams( + string memory json, + string memory path + ) public pure returns (EIP712Types.SetUsingAsCollateralParams memory) { + return + abi.decode( + vm.parseJsonType(json, path, schema_SetUsingAsCollateralParams), + (EIP712Types.SetUsingAsCollateralParams) + ); + } + + function deserializeSetUsingAsCollateralParamsArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.SetUsingAsCollateralParams[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_SetUsingAsCollateralParams), + (EIP712Types.SetUsingAsCollateralParams[]) + ); + } + + function serialize( + EIP712Types.UpdateUserRiskPremiumParams memory value + ) internal pure returns (string memory) { + return vm.serializeJsonType(schema_UpdateUserRiskPremiumParams, abi.encode(value)); + } + + function serialize( + EIP712Types.UpdateUserRiskPremiumParams memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return + vm.serializeJsonType(objectKey, valueKey, schema_UpdateUserRiskPremiumParams, abi.encode(value)); + } + + function deserializeUpdateUserRiskPremiumParams( + string memory json + ) public pure returns (EIP712Types.UpdateUserRiskPremiumParams memory) { + return + abi.decode( + vm.parseJsonType(json, schema_UpdateUserRiskPremiumParams), + (EIP712Types.UpdateUserRiskPremiumParams) + ); + } + + function deserializeUpdateUserRiskPremiumParams( + string memory json, + string memory path + ) public pure returns (EIP712Types.UpdateUserRiskPremiumParams memory) { + return + abi.decode( + vm.parseJsonType(json, path, schema_UpdateUserRiskPremiumParams), + (EIP712Types.UpdateUserRiskPremiumParams) + ); + } + + function deserializeUpdateUserRiskPremiumParamsArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.UpdateUserRiskPremiumParams[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_UpdateUserRiskPremiumParams), + (EIP712Types.UpdateUserRiskPremiumParams[]) + ); + } + + function serialize( + EIP712Types.UpdateUserDynamicConfigParams memory value + ) internal pure returns (string memory) { + return vm.serializeJsonType(schema_UpdateUserDynamicConfigParams, abi.encode(value)); + } + + function serialize( + EIP712Types.UpdateUserDynamicConfigParams memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return + vm.serializeJsonType( + objectKey, + valueKey, + schema_UpdateUserDynamicConfigParams, + abi.encode(value) + ); + } + + function deserializeUpdateUserDynamicConfigParams( + string memory json + ) public pure returns (EIP712Types.UpdateUserDynamicConfigParams memory) { + return + abi.decode( + vm.parseJsonType(json, schema_UpdateUserDynamicConfigParams), + (EIP712Types.UpdateUserDynamicConfigParams) + ); + } + + function deserializeUpdateUserDynamicConfigParams( + string memory json, + string memory path + ) public pure returns (EIP712Types.UpdateUserDynamicConfigParams memory) { + return + abi.decode( + vm.parseJsonType(json, path, schema_UpdateUserDynamicConfigParams), + (EIP712Types.UpdateUserDynamicConfigParams) + ); + } + + function deserializeUpdateUserDynamicConfigParamsArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.UpdateUserDynamicConfigParams[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_UpdateUserDynamicConfigParams), + (EIP712Types.UpdateUserDynamicConfigParams[]) + ); + } + + // ============ Action Bindings ============ + + function serialize(EIP712Types.SupplyAction memory value) internal pure returns (string memory) { + return vm.serializeJsonType(schema_SupplyAction, abi.encode(value)); + } + + function serialize( + EIP712Types.SupplyAction memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_SupplyAction, abi.encode(value)); + } + + function deserializeSupplyAction( + string memory json + ) public pure returns (EIP712Types.SupplyAction memory) { + return abi.decode(vm.parseJsonType(json, schema_SupplyAction), (EIP712Types.SupplyAction)); + } + + function deserializeSupplyAction( + string memory json, + string memory path + ) public pure returns (EIP712Types.SupplyAction memory) { + return + abi.decode(vm.parseJsonType(json, path, schema_SupplyAction), (EIP712Types.SupplyAction)); + } + + function deserializeSupplyActionArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.SupplyAction[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_SupplyAction), + (EIP712Types.SupplyAction[]) + ); + } + + function serialize( + EIP712Types.WithdrawAction memory value + ) internal pure returns (string memory) { + return vm.serializeJsonType(schema_WithdrawAction, abi.encode(value)); + } + + function serialize( + EIP712Types.WithdrawAction memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_WithdrawAction, abi.encode(value)); + } + + function deserializeWithdrawAction( + string memory json + ) public pure returns (EIP712Types.WithdrawAction memory) { + return abi.decode(vm.parseJsonType(json, schema_WithdrawAction), (EIP712Types.WithdrawAction)); + } + + function deserializeWithdrawAction( + string memory json, + string memory path + ) public pure returns (EIP712Types.WithdrawAction memory) { + return + abi.decode(vm.parseJsonType(json, path, schema_WithdrawAction), (EIP712Types.WithdrawAction)); + } + + function deserializeWithdrawActionArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.WithdrawAction[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_WithdrawAction), + (EIP712Types.WithdrawAction[]) + ); + } + + function serialize(EIP712Types.BorrowAction memory value) internal pure returns (string memory) { + return vm.serializeJsonType(schema_BorrowAction, abi.encode(value)); } function serialize( - EIP712Types.Supply memory value, + EIP712Types.BorrowAction memory value, string memory objectKey, string memory valueKey ) internal returns (string memory) { - return vm.serializeJsonType(objectKey, valueKey, schema_Supply, abi.encode(value)); + return vm.serializeJsonType(objectKey, valueKey, schema_BorrowAction, abi.encode(value)); } - function deserializeSupply(string memory json) public pure returns (EIP712Types.Supply memory) { - return abi.decode(vm.parseJsonType(json, schema_Supply), (EIP712Types.Supply)); + function deserializeBorrowAction( + string memory json + ) public pure returns (EIP712Types.BorrowAction memory) { + return abi.decode(vm.parseJsonType(json, schema_BorrowAction), (EIP712Types.BorrowAction)); } - function deserializeSupply( + function deserializeBorrowAction( string memory json, string memory path - ) public pure returns (EIP712Types.Supply memory) { - return abi.decode(vm.parseJsonType(json, path, schema_Supply), (EIP712Types.Supply)); + ) public pure returns (EIP712Types.BorrowAction memory) { + return + abi.decode(vm.parseJsonType(json, path, schema_BorrowAction), (EIP712Types.BorrowAction)); } - function deserializeSupplyArray( + function deserializeBorrowActionArray( string memory json, string memory path - ) public pure returns (EIP712Types.Supply[] memory) { - return abi.decode(vm.parseJsonTypeArray(json, path, schema_Supply), (EIP712Types.Supply[])); + ) public pure returns (EIP712Types.BorrowAction[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_BorrowAction), + (EIP712Types.BorrowAction[]) + ); } - function serialize(EIP712Types.Withdraw memory value) internal pure returns (string memory) { - return vm.serializeJsonType(schema_Withdraw, abi.encode(value)); + function serialize(EIP712Types.RepayAction memory value) internal pure returns (string memory) { + return vm.serializeJsonType(schema_RepayAction, abi.encode(value)); } function serialize( - EIP712Types.Withdraw memory value, + EIP712Types.RepayAction memory value, string memory objectKey, string memory valueKey ) internal returns (string memory) { - return vm.serializeJsonType(objectKey, valueKey, schema_Withdraw, abi.encode(value)); + return vm.serializeJsonType(objectKey, valueKey, schema_RepayAction, abi.encode(value)); } - function deserializeWithdraw( + function deserializeRepayAction( string memory json - ) public pure returns (EIP712Types.Withdraw memory) { - return abi.decode(vm.parseJsonType(json, schema_Withdraw), (EIP712Types.Withdraw)); + ) public pure returns (EIP712Types.RepayAction memory) { + return abi.decode(vm.parseJsonType(json, schema_RepayAction), (EIP712Types.RepayAction)); } - function deserializeWithdraw( + function deserializeRepayAction( string memory json, string memory path - ) public pure returns (EIP712Types.Withdraw memory) { - return abi.decode(vm.parseJsonType(json, path, schema_Withdraw), (EIP712Types.Withdraw)); + ) public pure returns (EIP712Types.RepayAction memory) { + return abi.decode(vm.parseJsonType(json, path, schema_RepayAction), (EIP712Types.RepayAction)); } - function deserializeWithdrawArray( + function deserializeRepayActionArray( string memory json, string memory path - ) public pure returns (EIP712Types.Withdraw[] memory) { - return abi.decode(vm.parseJsonTypeArray(json, path, schema_Withdraw), (EIP712Types.Withdraw[])); + ) public pure returns (EIP712Types.RepayAction[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_RepayAction), + (EIP712Types.RepayAction[]) + ); } - function serialize(EIP712Types.Borrow memory value) internal pure returns (string memory) { - return vm.serializeJsonType(schema_Borrow, abi.encode(value)); + function serialize( + EIP712Types.SetUsingAsCollateralAction memory value + ) internal pure returns (string memory) { + return vm.serializeJsonType(schema_SetUsingAsCollateralAction, abi.encode(value)); } function serialize( - EIP712Types.Borrow memory value, + EIP712Types.SetUsingAsCollateralAction memory value, string memory objectKey, string memory valueKey ) internal returns (string memory) { - return vm.serializeJsonType(objectKey, valueKey, schema_Borrow, abi.encode(value)); + return + vm.serializeJsonType(objectKey, valueKey, schema_SetUsingAsCollateralAction, abi.encode(value)); } - function deserializeBorrow(string memory json) public pure returns (EIP712Types.Borrow memory) { - return abi.decode(vm.parseJsonType(json, schema_Borrow), (EIP712Types.Borrow)); + function deserializeSetUsingAsCollateralAction( + string memory json + ) public pure returns (EIP712Types.SetUsingAsCollateralAction memory) { + return + abi.decode( + vm.parseJsonType(json, schema_SetUsingAsCollateralAction), + (EIP712Types.SetUsingAsCollateralAction) + ); } - function deserializeBorrow( + function deserializeSetUsingAsCollateralAction( string memory json, string memory path - ) public pure returns (EIP712Types.Borrow memory) { - return abi.decode(vm.parseJsonType(json, path, schema_Borrow), (EIP712Types.Borrow)); + ) public pure returns (EIP712Types.SetUsingAsCollateralAction memory) { + return + abi.decode( + vm.parseJsonType(json, path, schema_SetUsingAsCollateralAction), + (EIP712Types.SetUsingAsCollateralAction) + ); } - function deserializeBorrowArray( + function deserializeSetUsingAsCollateralActionArray( string memory json, string memory path - ) public pure returns (EIP712Types.Borrow[] memory) { - return abi.decode(vm.parseJsonTypeArray(json, path, schema_Borrow), (EIP712Types.Borrow[])); + ) public pure returns (EIP712Types.SetUsingAsCollateralAction[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_SetUsingAsCollateralAction), + (EIP712Types.SetUsingAsCollateralAction[]) + ); } - function serialize(EIP712Types.Repay memory value) internal pure returns (string memory) { - return vm.serializeJsonType(schema_Repay, abi.encode(value)); + function serialize( + EIP712Types.UpdateUserRiskPremiumAction memory value + ) internal pure returns (string memory) { + return vm.serializeJsonType(schema_UpdateUserRiskPremiumAction, abi.encode(value)); } function serialize( - EIP712Types.Repay memory value, + EIP712Types.UpdateUserRiskPremiumAction memory value, string memory objectKey, string memory valueKey ) internal returns (string memory) { - return vm.serializeJsonType(objectKey, valueKey, schema_Repay, abi.encode(value)); + return + vm.serializeJsonType(objectKey, valueKey, schema_UpdateUserRiskPremiumAction, abi.encode(value)); } - function deserializeRepay(string memory json) public pure returns (EIP712Types.Repay memory) { - return abi.decode(vm.parseJsonType(json, schema_Repay), (EIP712Types.Repay)); + function deserializeUpdateUserRiskPremiumAction( + string memory json + ) public pure returns (EIP712Types.UpdateUserRiskPremiumAction memory) { + return + abi.decode( + vm.parseJsonType(json, schema_UpdateUserRiskPremiumAction), + (EIP712Types.UpdateUserRiskPremiumAction) + ); } - function deserializeRepay( + function deserializeUpdateUserRiskPremiumAction( string memory json, string memory path - ) public pure returns (EIP712Types.Repay memory) { - return abi.decode(vm.parseJsonType(json, path, schema_Repay), (EIP712Types.Repay)); + ) public pure returns (EIP712Types.UpdateUserRiskPremiumAction memory) { + return + abi.decode( + vm.parseJsonType(json, path, schema_UpdateUserRiskPremiumAction), + (EIP712Types.UpdateUserRiskPremiumAction) + ); } - function deserializeRepayArray( + function deserializeUpdateUserRiskPremiumActionArray( string memory json, string memory path - ) public pure returns (EIP712Types.Repay[] memory) { - return abi.decode(vm.parseJsonTypeArray(json, path, schema_Repay), (EIP712Types.Repay[])); + ) public pure returns (EIP712Types.UpdateUserRiskPremiumAction[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_UpdateUserRiskPremiumAction), + (EIP712Types.UpdateUserRiskPremiumAction[]) + ); } function serialize( - EIP712Types.SetUsingAsCollateral memory value + EIP712Types.UpdateUserDynamicConfigAction memory value ) internal pure returns (string memory) { - return vm.serializeJsonType(schema_SetUsingAsCollateral, abi.encode(value)); + return vm.serializeJsonType(schema_UpdateUserDynamicConfigAction, abi.encode(value)); } function serialize( - EIP712Types.SetUsingAsCollateral memory value, + EIP712Types.UpdateUserDynamicConfigAction memory value, string memory objectKey, string memory valueKey ) internal returns (string memory) { return - vm.serializeJsonType(objectKey, valueKey, schema_SetUsingAsCollateral, abi.encode(value)); + vm.serializeJsonType( + objectKey, + valueKey, + schema_UpdateUserDynamicConfigAction, + abi.encode(value) + ); } - function deserializeSetUsingAsCollateral( + function deserializeUpdateUserDynamicConfigAction( string memory json - ) public pure returns (EIP712Types.SetUsingAsCollateral memory) { + ) public pure returns (EIP712Types.UpdateUserDynamicConfigAction memory) { return abi.decode( - vm.parseJsonType(json, schema_SetUsingAsCollateral), - (EIP712Types.SetUsingAsCollateral) + vm.parseJsonType(json, schema_UpdateUserDynamicConfigAction), + (EIP712Types.UpdateUserDynamicConfigAction) ); } - function deserializeSetUsingAsCollateral( + function deserializeUpdateUserDynamicConfigAction( string memory json, string memory path - ) public pure returns (EIP712Types.SetUsingAsCollateral memory) { + ) public pure returns (EIP712Types.UpdateUserDynamicConfigAction memory) { return abi.decode( - vm.parseJsonType(json, path, schema_SetUsingAsCollateral), - (EIP712Types.SetUsingAsCollateral) + vm.parseJsonType(json, path, schema_UpdateUserDynamicConfigAction), + (EIP712Types.UpdateUserDynamicConfigAction) ); } - function deserializeSetUsingAsCollateralArray( + function deserializeUpdateUserDynamicConfigActionArray( string memory json, string memory path - ) public pure returns (EIP712Types.SetUsingAsCollateral[] memory) { + ) public pure returns (EIP712Types.UpdateUserDynamicConfigAction[] memory) { return abi.decode( - vm.parseJsonTypeArray(json, path, schema_SetUsingAsCollateral), - (EIP712Types.SetUsingAsCollateral[]) + vm.parseJsonTypeArray(json, path, schema_UpdateUserDynamicConfigAction), + (EIP712Types.UpdateUserDynamicConfigAction[]) ); } + // ============ Permit2 Bindings ============ + function serialize( - EIP712Types.UpdateUserRiskPremium memory value + EIP712Types.TokenPermissions memory value ) internal pure returns (string memory) { - return vm.serializeJsonType(schema_UpdateUserRiskPremium, abi.encode(value)); + return vm.serializeJsonType(schema_TokenPermissions, abi.encode(value)); } function serialize( - EIP712Types.UpdateUserRiskPremium memory value, + EIP712Types.TokenPermissions memory value, string memory objectKey, string memory valueKey ) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_TokenPermissions, abi.encode(value)); + } + + function deserializeTokenPermissions( + string memory json + ) public pure returns (EIP712Types.TokenPermissions memory) { + return + abi.decode(vm.parseJsonType(json, schema_TokenPermissions), (EIP712Types.TokenPermissions)); + } + + function deserializeTokenPermissions( + string memory json, + string memory path + ) public pure returns (EIP712Types.TokenPermissions memory) { return - vm.serializeJsonType(objectKey, valueKey, schema_UpdateUserRiskPremium, abi.encode(value)); + abi.decode( + vm.parseJsonType(json, path, schema_TokenPermissions), + (EIP712Types.TokenPermissions) + ); } - function deserializeUpdateUserRiskPremium( + function deserializeTokenPermissionsArray( + string memory json, + string memory path + ) public pure returns (EIP712Types.TokenPermissions[] memory) { + return + abi.decode( + vm.parseJsonTypeArray(json, path, schema_TokenPermissions), + (EIP712Types.TokenPermissions[]) + ); + } + + function serialize( + EIP712Types.PermitWitnessTransferFromSupplyAction memory value + ) internal pure returns (string memory) { + return vm.serializeJsonType(schema_PermitWitnessTransferFromSupplyAction, abi.encode(value)); + } + + function serialize( + EIP712Types.PermitWitnessTransferFromSupplyAction memory value, + string memory objectKey, + string memory valueKey + ) internal returns (string memory) { + return + vm.serializeJsonType( + objectKey, + valueKey, + schema_PermitWitnessTransferFromSupplyAction, + abi.encode(value) + ); + } + + function deserializePermitWitnessTransferFromSupplyAction( string memory json - ) public pure returns (EIP712Types.UpdateUserRiskPremium memory) { + ) public pure returns (EIP712Types.PermitWitnessTransferFromSupplyAction memory) { return abi.decode( - vm.parseJsonType(json, schema_UpdateUserRiskPremium), - (EIP712Types.UpdateUserRiskPremium) + vm.parseJsonType(json, schema_PermitWitnessTransferFromSupplyAction), + (EIP712Types.PermitWitnessTransferFromSupplyAction) ); } - function deserializeUpdateUserRiskPremium( + function deserializePermitWitnessTransferFromSupplyAction( string memory json, string memory path - ) public pure returns (EIP712Types.UpdateUserRiskPremium memory) { + ) public pure returns (EIP712Types.PermitWitnessTransferFromSupplyAction memory) { return abi.decode( - vm.parseJsonType(json, path, schema_UpdateUserRiskPremium), - (EIP712Types.UpdateUserRiskPremium) + vm.parseJsonType(json, path, schema_PermitWitnessTransferFromSupplyAction), + (EIP712Types.PermitWitnessTransferFromSupplyAction) ); } - function deserializeUpdateUserRiskPremiumArray( + function deserializePermitWitnessTransferFromSupplyActionArray( string memory json, string memory path - ) public pure returns (EIP712Types.UpdateUserRiskPremium[] memory) { + ) public pure returns (EIP712Types.PermitWitnessTransferFromSupplyAction[] memory) { return abi.decode( - vm.parseJsonTypeArray(json, path, schema_UpdateUserRiskPremium), - (EIP712Types.UpdateUserRiskPremium[]) + vm.parseJsonTypeArray(json, path, schema_PermitWitnessTransferFromSupplyAction), + (EIP712Types.PermitWitnessTransferFromSupplyAction[]) ); } function serialize( - EIP712Types.UpdateUserDynamicConfig memory value + EIP712Types.PermitWitnessTransferFromRepayAction memory value ) internal pure returns (string memory) { - return vm.serializeJsonType(schema_UpdateUserDynamicConfig, abi.encode(value)); + return vm.serializeJsonType(schema_PermitWitnessTransferFromRepayAction, abi.encode(value)); } function serialize( - EIP712Types.UpdateUserDynamicConfig memory value, + EIP712Types.PermitWitnessTransferFromRepayAction memory value, string memory objectKey, string memory valueKey ) internal returns (string memory) { return - vm.serializeJsonType(objectKey, valueKey, schema_UpdateUserDynamicConfig, abi.encode(value)); + vm.serializeJsonType( + objectKey, + valueKey, + schema_PermitWitnessTransferFromRepayAction, + abi.encode(value) + ); } - function deserializeUpdateUserDynamicConfig( + function deserializePermitWitnessTransferFromRepayAction( string memory json - ) public pure returns (EIP712Types.UpdateUserDynamicConfig memory) { + ) public pure returns (EIP712Types.PermitWitnessTransferFromRepayAction memory) { return abi.decode( - vm.parseJsonType(json, schema_UpdateUserDynamicConfig), - (EIP712Types.UpdateUserDynamicConfig) + vm.parseJsonType(json, schema_PermitWitnessTransferFromRepayAction), + (EIP712Types.PermitWitnessTransferFromRepayAction) ); } - function deserializeUpdateUserDynamicConfig( + function deserializePermitWitnessTransferFromRepayAction( string memory json, string memory path - ) public pure returns (EIP712Types.UpdateUserDynamicConfig memory) { + ) public pure returns (EIP712Types.PermitWitnessTransferFromRepayAction memory) { return abi.decode( - vm.parseJsonType(json, path, schema_UpdateUserDynamicConfig), - (EIP712Types.UpdateUserDynamicConfig) + vm.parseJsonType(json, path, schema_PermitWitnessTransferFromRepayAction), + (EIP712Types.PermitWitnessTransferFromRepayAction) ); } - function deserializeUpdateUserDynamicConfigArray( + function deserializePermitWitnessTransferFromRepayActionArray( string memory json, string memory path - ) public pure returns (EIP712Types.UpdateUserDynamicConfig[] memory) { + ) public pure returns (EIP712Types.PermitWitnessTransferFromRepayAction[] memory) { return abi.decode( - vm.parseJsonTypeArray(json, path, schema_UpdateUserDynamicConfig), - (EIP712Types.UpdateUserDynamicConfig[]) + vm.parseJsonTypeArray(json, path, schema_PermitWitnessTransferFromRepayAction), + (EIP712Types.PermitWitnessTransferFromRepayAction[]) ); } } diff --git a/tests/unit/misc/BatchEIP712.t.sol b/tests/unit/misc/BatchEIP712.t.sol new file mode 100644 index 000000000..6cde914e7 --- /dev/null +++ b/tests/unit/misc/BatchEIP712.t.sol @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: UNLICENSED +// Copyright (c) 2025 Aave Labs +pragma solidity ^0.8.0; + +import {Test} from 'forge-std/Test.sol'; + +import {ISignatureGateway} from 'src/position-manager/interfaces/ISignatureGateway.sol'; +import {BatchEIP712} from 'src/position-manager/libraries/BatchEIP712.sol'; + +contract BatchEIP712Test is Test { + function test_constants_supplyParamsTypehash() public pure { + assertEq(BatchEIP712.SUPPLY_PARAMS_TYPEHASH, vm.eip712HashType('SupplyParams')); + } + + function test_constants_withdrawParamsTypehash() public pure { + assertEq(BatchEIP712.WITHDRAW_PARAMS_TYPEHASH, vm.eip712HashType('WithdrawParams')); + } + + function test_constants_borrowParamsTypehash() public pure { + assertEq(BatchEIP712.BORROW_PARAMS_TYPEHASH, vm.eip712HashType('BorrowParams')); + } + + function test_constants_repayParamsTypehash() public pure { + assertEq(BatchEIP712.REPAY_PARAMS_TYPEHASH, vm.eip712HashType('RepayParams')); + } + + function test_constants_setUsingAsCollateralParamsTypehash() public pure { + assertEq( + BatchEIP712.SET_USING_AS_COLLATERAL_PARAMS_TYPEHASH, + vm.eip712HashType('SetUsingAsCollateralParams') + ); + } + + function test_constants_updateUserRiskPremiumParamsTypehash() public pure { + assertEq( + BatchEIP712.UPDATE_USER_RISK_PREMIUM_PARAMS_TYPEHASH, + vm.eip712HashType('UpdateUserRiskPremiumParams') + ); + } + + function test_constants_updateUserDynamicConfigParamsTypehash() public pure { + assertEq( + BatchEIP712.UPDATE_USER_DYNAMIC_CONFIG_PARAMS_TYPEHASH, + vm.eip712HashType('UpdateUserDynamicConfigParams') + ); + } + + function test_buildBatchTypeString_singleSupply() public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(SupplyParams action0,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_singleWithdraw() public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Withdraw); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(WithdrawParams action0,address onBehalfOf,uint256 nonce,uint256 deadline)WithdrawParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_singleBorrow() public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Borrow); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(BorrowParams action0,address onBehalfOf,uint256 nonce,uint256 deadline)BorrowParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_singleRepay() public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Repay); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(RepayParams action0,address onBehalfOf,uint256 nonce,uint256 deadline)RepayParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_singleSetUsingAsCollateral() public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(SetUsingAsCollateralParams action0,address onBehalfOf,uint256 nonce,uint256 deadline)SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)' + ); + } + + function test_buildBatchTypeString_singleUpdateUserRiskPremium() public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(UpdateUserRiskPremiumParams action0,address onBehalfOf,uint256 nonce,uint256 deadline)UpdateUserRiskPremiumParams(address spoke)' + ); + } + + function test_buildBatchTypeString_singleUpdateUserDynamicConfig() public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(UpdateUserDynamicConfigParams action0,address onBehalfOf,uint256 nonce,uint256 deadline)UpdateUserDynamicConfigParams(address spoke)' + ); + } + + function test_buildBatchTypeString_supplyWithdraw() public pure { + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(SupplyParams action0,WithdrawParams action1,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyParams(address spoke,uint256 reserveId,uint256 amount)WithdrawParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_borrowRepay() public pure { + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Borrow); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Repay); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(BorrowParams action0,RepayParams action1,address onBehalfOf,uint256 nonce,uint256 deadline)BorrowParams(address spoke,uint256 reserveId,uint256 amount)RepayParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_duplicateActions() public pure { + uint8[] memory actionTypes = new uint8[](3); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[2] = uint8(ISignatureGateway.ActionType.Withdraw); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + assertEq( + typeString, + 'Batch(SupplyParams action0,SupplyParams action1,WithdrawParams action2,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyParams(address spoke,uint256 reserveId,uint256 amount)WithdrawParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + function test_buildBatchTypeString_allActionsAlphabeticalOrder() public pure { + uint8[] memory actionTypes = new uint8[](7); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + actionTypes[2] = uint8(ISignatureGateway.ActionType.Borrow); + actionTypes[3] = uint8(ISignatureGateway.ActionType.Repay); + actionTypes[4] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); + actionTypes[5] = uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium); + actionTypes[6] = uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig); + + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + + string + memory expected = 'Batch(SupplyParams action0,WithdrawParams action1,BorrowParams action2,RepayParams action3,SetUsingAsCollateralParams action4,UpdateUserRiskPremiumParams action5,UpdateUserDynamicConfigParams action6,address onBehalfOf,uint256 nonce,uint256 deadline)BorrowParams(address spoke,uint256 reserveId,uint256 amount)RepayParams(address spoke,uint256 reserveId,uint256 amount)SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)SupplyParams(address spoke,uint256 reserveId,uint256 amount)UpdateUserDynamicConfigParams(address spoke)UpdateUserRiskPremiumParams(address spoke)WithdrawParams(address spoke,uint256 reserveId,uint256 amount)'; + assertEq(typeString, expected); + } + + function test_buildBatchTypeHash_matchesKeccak() public pure { + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + + bytes32 typeHash = BatchEIP712.buildBatchTypeHash(actionTypes); + string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); + + assertEq(typeHash, keccak256(bytes(typeString))); + } + + function test_hashAction_supply_fuzz(ISignatureGateway.SupplyParams calldata params) public pure { + bytes memory actionData = abi.encode(params); + bytes32 libHash = BatchEIP712.hashAction( + uint8(ISignatureGateway.ActionType.Supply), + actionData + ); + + assertEq(libHash, vm.eip712HashStruct('SupplyParams', abi.encode(params))); + } + + function test_hashAction_withdraw_fuzz( + ISignatureGateway.WithdrawParams calldata params + ) public pure { + bytes memory actionData = abi.encode(params); + bytes32 libHash = BatchEIP712.hashAction( + uint8(ISignatureGateway.ActionType.Withdraw), + actionData + ); + + assertEq(libHash, vm.eip712HashStruct('WithdrawParams', abi.encode(params))); + } + + function test_hashAction_borrow_fuzz(ISignatureGateway.BorrowParams calldata params) public pure { + bytes memory actionData = abi.encode(params); + bytes32 libHash = BatchEIP712.hashAction( + uint8(ISignatureGateway.ActionType.Borrow), + actionData + ); + + assertEq(libHash, vm.eip712HashStruct('BorrowParams', abi.encode(params))); + } + + function test_hashAction_repay_fuzz(ISignatureGateway.RepayParams calldata params) public pure { + bytes memory actionData = abi.encode(params); + bytes32 libHash = BatchEIP712.hashAction(uint8(ISignatureGateway.ActionType.Repay), actionData); + + assertEq(libHash, vm.eip712HashStruct('RepayParams', abi.encode(params))); + } + + function test_hashAction_setUsingAsCollateral_fuzz( + ISignatureGateway.SetUsingAsCollateralParams calldata params + ) public pure { + bytes memory actionData = abi.encode(params); + bytes32 libHash = BatchEIP712.hashAction( + uint8(ISignatureGateway.ActionType.SetUsingAsCollateral), + actionData + ); + + assertEq(libHash, vm.eip712HashStruct('SetUsingAsCollateralParams', abi.encode(params))); + } + + function test_hashAction_updateUserRiskPremium_fuzz( + ISignatureGateway.UpdateUserRiskPremiumParams calldata params + ) public pure { + bytes memory actionData = abi.encode(params); + bytes32 libHash = BatchEIP712.hashAction( + uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium), + actionData + ); + + assertEq(libHash, vm.eip712HashStruct('UpdateUserRiskPremiumParams', abi.encode(params))); + } + + function test_hashAction_updateUserDynamicConfig_fuzz( + ISignatureGateway.UpdateUserDynamicConfigParams calldata params + ) public pure { + bytes memory actionData = abi.encode(params); + bytes32 libHash = BatchEIP712.hashAction( + uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig), + actionData + ); + + assertEq(libHash, vm.eip712HashStruct('UpdateUserDynamicConfigParams', abi.encode(params))); + } + + function test_hashBatch_singleSupply_fuzz( + ISignatureGateway.SupplyParams calldata params, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) public pure { + uint8[] memory actionTypes = new uint8[](1); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + + bytes[] memory actionData = new bytes[](1); + actionData[0] = abi.encode(params); + + bytes32 libHash = BatchEIP712.hashBatch(actionTypes, actionData, onBehalfOf, nonce, deadline); + + bytes32 typeHash = BatchEIP712.buildBatchTypeHash(actionTypes); + bytes32 paramsHash = vm.eip712HashStruct('SupplyParams', abi.encode(params)); + bytes32 expected = keccak256( + bytes.concat(abi.encode(typeHash), abi.encode(paramsHash, onBehalfOf, nonce, deadline)) + ); + + assertEq(libHash, expected); + } + + function test_hashBatch_supplyWithdraw_fuzz( + ISignatureGateway.SupplyParams calldata supplyParams, + ISignatureGateway.WithdrawParams calldata withdrawParams, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) public pure { + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode(supplyParams); + actionData[1] = abi.encode(withdrawParams); + + bytes32 libHash = BatchEIP712.hashBatch(actionTypes, actionData, onBehalfOf, nonce, deadline); + + bytes32 typeHash = BatchEIP712.buildBatchTypeHash(actionTypes); + bytes32 supplyHash = vm.eip712HashStruct('SupplyParams', abi.encode(supplyParams)); + bytes32 withdrawHash = vm.eip712HashStruct('WithdrawParams', abi.encode(withdrawParams)); + bytes32 expected = keccak256( + bytes.concat( + abi.encode(typeHash), + abi.encode(supplyHash), + abi.encode(withdrawHash), + abi.encode(onBehalfOf, nonce, deadline) + ) + ); + + assertEq(libHash, expected); + } + + function test_hashBatch_borrowRepay_fuzz( + ISignatureGateway.BorrowParams calldata borrowParams, + ISignatureGateway.RepayParams calldata repayParams, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) public pure { + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Borrow); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Repay); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode(borrowParams); + actionData[1] = abi.encode(repayParams); + + bytes32 libHash = BatchEIP712.hashBatch(actionTypes, actionData, onBehalfOf, nonce, deadline); + + bytes32 typeHash = BatchEIP712.buildBatchTypeHash(actionTypes); + bytes32 borrowHash = vm.eip712HashStruct('BorrowParams', abi.encode(borrowParams)); + bytes32 repayHash = vm.eip712HashStruct('RepayParams', abi.encode(repayParams)); + bytes32 expected = keccak256( + bytes.concat( + abi.encode(typeHash), + abi.encode(borrowHash), + abi.encode(repayHash), + abi.encode(onBehalfOf, nonce, deadline) + ) + ); + + assertEq(libHash, expected); + } + + function test_hashBatch_threeActions_fuzz( + ISignatureGateway.SupplyParams calldata supplyParams, + ISignatureGateway.RepayParams calldata repayParams, + ISignatureGateway.SetUsingAsCollateralParams calldata collateralParams, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) public pure { + uint8[] memory actionTypes = new uint8[](3); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Repay); + actionTypes[2] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); + + bytes[] memory actionData = new bytes[](3); + actionData[0] = abi.encode(supplyParams); + actionData[1] = abi.encode(repayParams); + actionData[2] = abi.encode(collateralParams); + + bytes32 libHash = BatchEIP712.hashBatch(actionTypes, actionData, onBehalfOf, nonce, deadline); + + bytes32 typeHash = BatchEIP712.buildBatchTypeHash(actionTypes); + bytes32 supplyHash = vm.eip712HashStruct('SupplyParams', abi.encode(supplyParams)); + bytes32 repayHash = vm.eip712HashStruct('RepayParams', abi.encode(repayParams)); + bytes32 collateralHash = vm.eip712HashStruct( + 'SetUsingAsCollateralParams', + abi.encode(collateralParams) + ); + bytes32 expected = keccak256( + bytes.concat( + abi.encode(typeHash), + abi.encode(supplyHash), + abi.encode(repayHash), + abi.encode(collateralHash), + abi.encode(onBehalfOf, nonce, deadline) + ) + ); + + assertEq(libHash, expected); + } + + function test_hashBatch_duplicateSupply_fuzz( + ISignatureGateway.SupplyParams calldata supply1, + ISignatureGateway.SupplyParams calldata supply2, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) public pure { + uint8[] memory actionTypes = new uint8[](2); + actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); + actionTypes[1] = uint8(ISignatureGateway.ActionType.Supply); + + bytes[] memory actionData = new bytes[](2); + actionData[0] = abi.encode(supply1); + actionData[1] = abi.encode(supply2); + + bytes32 libHash = BatchEIP712.hashBatch(actionTypes, actionData, onBehalfOf, nonce, deadline); + + bytes32 typeHash = BatchEIP712.buildBatchTypeHash(actionTypes); + bytes32 supply1Hash = vm.eip712HashStruct('SupplyParams', abi.encode(supply1)); + bytes32 supply2Hash = vm.eip712HashStruct('SupplyParams', abi.encode(supply2)); + bytes32 expected = keccak256( + bytes.concat( + abi.encode(typeHash), + abi.encode(supply1Hash), + abi.encode(supply2Hash), + abi.encode(onBehalfOf, nonce, deadline) + ) + ); + + assertEq(libHash, expected); + } + + function test_hashAction_revertsOnInvalidActionType() public { + bytes memory actionData = abi.encode(address(0), uint256(0), uint256(0)); + vm.expectRevert(abi.encodeWithSelector(BatchEIP712.InvalidActionType.selector, 255)); + this.externalHashAction(255, actionData); + } + + function externalHashAction( + uint8 actionType, + bytes memory actionData + ) external pure returns (bytes32) { + return BatchEIP712.hashAction(actionType, actionData); + } +} diff --git a/tests/unit/misc/EIP712Hash.t.sol b/tests/unit/misc/EIP712Hash.t.sol index 515175af4..ce47c2fe7 100644 --- a/tests/unit/misc/EIP712Hash.t.sol +++ b/tests/unit/misc/EIP712Hash.t.sol @@ -14,239 +14,181 @@ contract EIP712HashTest is Test { using PositionManagerEIP712Hash for *; using SpokeEIP712Hash for *; - function test_constants() public pure { - assertEq( - PositionManagerEIP712Hash.SUPPLY_TYPEHASH, - keccak256( - 'Supply(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) - ); - assertEq(PositionManagerEIP712Hash.SUPPLY_TYPEHASH, vm.eip712HashType('Supply')); + function test_constants_supplyTypehash() public pure { + assertEq(PositionManagerEIP712Hash.SUPPLY_TYPEHASH, vm.eip712HashType('SupplyAction')); + } - assertEq( - PositionManagerEIP712Hash.WITHDRAW_TYPEHASH, - keccak256( - 'Withdraw(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) - ); - assertEq(PositionManagerEIP712Hash.WITHDRAW_TYPEHASH, vm.eip712HashType('Withdraw')); + function test_constants_supplyParamsTypehash() public pure { + assertEq(PositionManagerEIP712Hash.SUPPLY_PARAMS_TYPEHASH, vm.eip712HashType('SupplyParams')); + } - assertEq( - PositionManagerEIP712Hash.BORROW_TYPEHASH, - keccak256( - 'Borrow(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) - ); - assertEq(PositionManagerEIP712Hash.BORROW_TYPEHASH, vm.eip712HashType('Borrow')); + function test_constants_withdrawTypehash() public pure { + assertEq(PositionManagerEIP712Hash.WITHDRAW_TYPEHASH, vm.eip712HashType('WithdrawAction')); + } + function test_constants_withdrawParamsTypehash() public pure { assertEq( - PositionManagerEIP712Hash.REPAY_TYPEHASH, - keccak256( - 'Repay(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) + PositionManagerEIP712Hash.WITHDRAW_PARAMS_TYPEHASH, + vm.eip712HashType('WithdrawParams') ); - assertEq(PositionManagerEIP712Hash.REPAY_TYPEHASH, vm.eip712HashType('Repay')); + } + function test_constants_borrowTypehash() public pure { + assertEq(PositionManagerEIP712Hash.BORROW_TYPEHASH, vm.eip712HashType('BorrowAction')); + } + + function test_constants_borrowParamsTypehash() public pure { + assertEq(PositionManagerEIP712Hash.BORROW_PARAMS_TYPEHASH, vm.eip712HashType('BorrowParams')); + } + + function test_constants_repayTypehash() public pure { + assertEq(PositionManagerEIP712Hash.REPAY_TYPEHASH, vm.eip712HashType('RepayAction')); + } + + function test_constants_repayParamsTypehash() public pure { + assertEq(PositionManagerEIP712Hash.REPAY_PARAMS_TYPEHASH, vm.eip712HashType('RepayParams')); + } + + function test_constants_setUsingAsCollateralTypehash() public pure { assertEq( PositionManagerEIP712Hash.SET_USING_AS_COLLATERAL_TYPEHASH, - keccak256( - 'SetUsingAsCollateral(address spoke,uint256 reserveId,bool useAsCollateral,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) - ); - assertEq( - PositionManagerEIP712Hash.SET_USING_AS_COLLATERAL_TYPEHASH, - vm.eip712HashType('SetUsingAsCollateral') + vm.eip712HashType('SetUsingAsCollateralAction') ); + } + function test_constants_setUsingAsCollateralParamsTypehash() public pure { assertEq( - PositionManagerEIP712Hash.UPDATE_USER_RISK_PREMIUM_TYPEHASH, - keccak256('UpdateUserRiskPremium(address spoke,address user,uint256 nonce,uint256 deadline)') + PositionManagerEIP712Hash.SET_USING_AS_COLLATERAL_PARAMS_TYPEHASH, + vm.eip712HashType('SetUsingAsCollateralParams') ); + } + + function test_constants_updateUserRiskPremiumTypehash() public pure { assertEq( PositionManagerEIP712Hash.UPDATE_USER_RISK_PREMIUM_TYPEHASH, - vm.eip712HashType('UpdateUserRiskPremium') + vm.eip712HashType('UpdateUserRiskPremiumAction') ); + } + function test_constants_updateUserRiskPremiumParamsTypehash() public pure { assertEq( - PositionManagerEIP712Hash.UPDATE_USER_DYNAMIC_CONFIG_TYPEHASH, - keccak256( - 'UpdateUserDynamicConfig(address spoke,address user,uint256 nonce,uint256 deadline)' - ) + PositionManagerEIP712Hash.UPDATE_USER_RISK_PREMIUM_PARAMS_TYPEHASH, + vm.eip712HashType('UpdateUserRiskPremiumParams') ); + } + + function test_constants_updateUserDynamicConfigTypehash() public pure { assertEq( PositionManagerEIP712Hash.UPDATE_USER_DYNAMIC_CONFIG_TYPEHASH, - vm.eip712HashType('UpdateUserDynamicConfig') + vm.eip712HashType('UpdateUserDynamicConfigAction') ); + } + function test_constants_updateUserDynamicConfigParamsTypehash() public pure { assertEq( - SpokeEIP712Hash.SET_USER_POSITION_MANAGERS_TYPEHASH, - keccak256( - 'SetUserPositionManagers(address user,PositionManagerUpdate[] updates,uint256 nonce,uint256 deadline)PositionManagerUpdate(address positionManager,bool approve)' - ) + PositionManagerEIP712Hash.UPDATE_USER_DYNAMIC_CONFIG_PARAMS_TYPEHASH, + vm.eip712HashType('UpdateUserDynamicConfigParams') ); + } + + function test_constants_setUserPositionManagersTypehash() public pure { assertEq( SpokeEIP712Hash.SET_USER_POSITION_MANAGERS_TYPEHASH, vm.eip712HashType('SetUserPositionManagers') ); + } - assertEq( - SpokeEIP712Hash.POSITION_MANAGER_UPDATE, - keccak256('PositionManagerUpdate(address positionManager,bool approve)') - ); + function test_constants_positionManagerUpdateTypehash() public pure { assertEq(SpokeEIP712Hash.POSITION_MANAGER_UPDATE, vm.eip712HashType('PositionManagerUpdate')); } - function test_hash_supply_fuzz(ISignatureGateway.Supply calldata params) public pure { - bytes32 expectedHash = keccak256( - abi.encode( - PositionManagerEIP712Hash.SUPPLY_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline - ) - ); + function test_hash_supplyAction_fuzz(ISignatureGateway.SupplyAction calldata action) public pure { + assertEq(action.hash(), vm.eip712HashStruct('SupplyAction', abi.encode(action))); + } - assertEq(params.hash(), expectedHash); - assertEq(params.hash(), vm.eip712HashStruct('Supply', abi.encode(params))); - } - - function test_hash_withdraw_fuzz(ISignatureGateway.Withdraw calldata params) public pure { - bytes32 expectedHash = keccak256( - abi.encode( - PositionManagerEIP712Hash.WITHDRAW_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline - ) - ); + function test_hash_supplyParams_fuzz(ISignatureGateway.SupplyParams calldata params) public pure { + assertEq(params.hash(), vm.eip712HashStruct('SupplyParams', abi.encode(params))); + } - assertEq(params.hash(), expectedHash); - assertEq(params.hash(), vm.eip712HashStruct('Withdraw', abi.encode(params))); - } - - function test_hash_borrow_fuzz(ISignatureGateway.Borrow calldata params) public pure { - bytes32 expectedHash = keccak256( - abi.encode( - PositionManagerEIP712Hash.BORROW_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline - ) - ); + function test_hash_withdrawAction_fuzz( + ISignatureGateway.WithdrawAction calldata action + ) public pure { + assertEq(action.hash(), vm.eip712HashStruct('WithdrawAction', abi.encode(action))); + } - assertEq(params.hash(), expectedHash); - assertEq(params.hash(), vm.eip712HashStruct('Borrow', abi.encode(params))); - } - - function test_hash_repay_fuzz(ISignatureGateway.Repay calldata params) public pure { - bytes32 expectedHash = keccak256( - abi.encode( - PositionManagerEIP712Hash.REPAY_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline - ) - ); + function test_hash_withdrawParams_fuzz( + ISignatureGateway.WithdrawParams calldata params + ) public pure { + assertEq(params.hash(), vm.eip712HashStruct('WithdrawParams', abi.encode(params))); + } + + function test_hash_borrowAction_fuzz(ISignatureGateway.BorrowAction calldata action) public pure { + assertEq(action.hash(), vm.eip712HashStruct('BorrowAction', abi.encode(action))); + } + + function test_hash_borrowParams_fuzz(ISignatureGateway.BorrowParams calldata params) public pure { + assertEq(params.hash(), vm.eip712HashStruct('BorrowParams', abi.encode(params))); + } - assertEq(params.hash(), expectedHash); - assertEq(params.hash(), vm.eip712HashStruct('Repay', abi.encode(params))); + function test_hash_repayAction_fuzz(ISignatureGateway.RepayAction calldata action) public pure { + assertEq(action.hash(), vm.eip712HashStruct('RepayAction', abi.encode(action))); } - function test_hash_setUsingAsCollateral_fuzz( - ISignatureGateway.SetUsingAsCollateral calldata params + function test_hash_repayParams_fuzz(ISignatureGateway.RepayParams calldata params) public pure { + assertEq(params.hash(), vm.eip712HashStruct('RepayParams', abi.encode(params))); + } + + function test_hash_setUsingAsCollateralAction_fuzz( + ISignatureGateway.SetUsingAsCollateralAction calldata action ) public pure { - bytes32 expectedHash = keccak256( - abi.encode( - PositionManagerEIP712Hash.SET_USING_AS_COLLATERAL_TYPEHASH, - params.spoke, - params.reserveId, - params.useAsCollateral, - params.onBehalfOf, - params.nonce, - params.deadline - ) - ); + assertEq(action.hash(), vm.eip712HashStruct('SetUsingAsCollateralAction', abi.encode(action))); + } - assertEq(params.hash(), expectedHash); - assertEq(params.hash(), vm.eip712HashStruct('SetUsingAsCollateral', abi.encode(params))); + function test_hash_setUsingAsCollateralParams_fuzz( + ISignatureGateway.SetUsingAsCollateralParams calldata params + ) public pure { + assertEq(params.hash(), vm.eip712HashStruct('SetUsingAsCollateralParams', abi.encode(params))); } - function test_hash_updateUserRiskPremium_fuzz( - ISignatureGateway.UpdateUserRiskPremium calldata params + function test_hash_updateUserRiskPremiumAction_fuzz( + ISignatureGateway.UpdateUserRiskPremiumAction calldata action ) public pure { - bytes32 expectedHash = keccak256( - abi.encode( - PositionManagerEIP712Hash.UPDATE_USER_RISK_PREMIUM_TYPEHASH, - params.spoke, - params.user, - params.nonce, - params.deadline - ) - ); + assertEq(action.hash(), vm.eip712HashStruct('UpdateUserRiskPremiumAction', abi.encode(action))); + } - assertEq(params.hash(), expectedHash); - assertEq(params.hash(), vm.eip712HashStruct('UpdateUserRiskPremium', abi.encode(params))); + function test_hash_updateUserRiskPremiumParams_fuzz( + ISignatureGateway.UpdateUserRiskPremiumParams calldata params + ) public pure { + assertEq(params.hash(), vm.eip712HashStruct('UpdateUserRiskPremiumParams', abi.encode(params))); } - function test_hash_updateUserDynamicConfig_fuzz( - ISignatureGateway.UpdateUserDynamicConfig calldata params + function test_hash_updateUserDynamicConfigAction_fuzz( + ISignatureGateway.UpdateUserDynamicConfigAction calldata action ) public pure { - bytes32 expectedHash = keccak256( - abi.encode( - PositionManagerEIP712Hash.UPDATE_USER_DYNAMIC_CONFIG_TYPEHASH, - params.spoke, - params.user, - params.nonce, - params.deadline - ) + assertEq( + action.hash(), + vm.eip712HashStruct('UpdateUserDynamicConfigAction', abi.encode(action)) ); + } - assertEq(params.hash(), expectedHash); - assertEq(params.hash(), vm.eip712HashStruct('UpdateUserDynamicConfig', abi.encode(params))); + function test_hash_updateUserDynamicConfigParams_fuzz( + ISignatureGateway.UpdateUserDynamicConfigParams calldata params + ) public pure { + assertEq( + params.hash(), + vm.eip712HashStruct('UpdateUserDynamicConfigParams', abi.encode(params)) + ); } function test_hash_setUserPositionManagers_fuzz( ISpoke.SetUserPositionManagers calldata params ) public pure { - bytes32[] memory updatesHashes = new bytes32[](params.updates.length); - for (uint256 i = 0; i < updatesHashes.length; ++i) { - updatesHashes[i] = params.updates[i].hash(); - } - - bytes32 expectedHash = keccak256( - abi.encode( - SpokeEIP712Hash.SET_USER_POSITION_MANAGERS_TYPEHASH, - params.user, - keccak256(abi.encodePacked(updatesHashes)), - params.nonce, - params.deadline - ) - ); - - assertEq(params.hash(), expectedHash); assertEq(params.hash(), vm.eip712HashStruct('SetUserPositionManagers', abi.encode(params))); } function test_hash_positionManagerUpdate_fuzz( ISpoke.PositionManagerUpdate calldata params ) public pure { - bytes32 expectedHash = keccak256( - abi.encode(SpokeEIP712Hash.POSITION_MANAGER_UPDATE, params.positionManager, params.approve) - ); - - assertEq(params.hash(), expectedHash); assertEq(params.hash(), vm.eip712HashStruct('PositionManagerUpdate', abi.encode(params))); } } diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol index b2e4f6654..55854ac58 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol @@ -25,164 +25,183 @@ contract SignatureGatewayBaseTest is SpokeBase { return abi.encodePacked(r, s, v); } - function _supplyData( + function _supplyAction( ISpoke spoke, address who, uint256 deadline - ) internal returns (ISignatureGateway.Supply memory) { + ) internal returns (ISignatureGateway.SupplyAction memory) { return - ISignatureGateway.Supply({ - spoke: address(spoke), - reserveId: _randomReserveId(spoke), - amount: vm.randomUint(1, MAX_SUPPLY_AMOUNT), + ISignatureGateway.SupplyAction({ onBehalfOf: who, nonce: gateway.nonces(who, _randomNonceKey()), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.SupplyParams({ + spoke: address(spoke), + reserveId: _randomReserveId(spoke), + amount: vm.randomUint(1, MAX_SUPPLY_AMOUNT) + }) }); } - function _withdrawData( + function _withdrawAction( ISpoke spoke, address who, uint256 deadline - ) internal returns (ISignatureGateway.Withdraw memory) { + ) internal returns (ISignatureGateway.WithdrawAction memory) { return - ISignatureGateway.Withdraw({ - spoke: address(spoke), - reserveId: _randomReserveId(spoke), - amount: vm.randomUint(1, MAX_SUPPLY_AMOUNT), + ISignatureGateway.WithdrawAction({ onBehalfOf: who, nonce: gateway.nonces(who, _randomNonceKey()), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.WithdrawParams({ + spoke: address(spoke), + reserveId: _randomReserveId(spoke), + amount: vm.randomUint(1, MAX_SUPPLY_AMOUNT) + }) }); } - function _borrowData( + function _borrowAction( ISpoke spoke, address who, uint256 deadline - ) internal returns (ISignatureGateway.Borrow memory) { + ) internal returns (ISignatureGateway.BorrowAction memory) { return - ISignatureGateway.Borrow({ - spoke: address(spoke), - reserveId: _randomReserveId(spoke), - amount: vm.randomUint(1, MAX_SUPPLY_AMOUNT), + ISignatureGateway.BorrowAction({ onBehalfOf: who, nonce: gateway.nonces(who, _randomNonceKey()), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.BorrowParams({ + spoke: address(spoke), + reserveId: _randomReserveId(spoke), + amount: vm.randomUint(1, MAX_SUPPLY_AMOUNT) + }) }); } - function _repayData( + function _repayAction( ISpoke spoke, address who, uint256 deadline - ) internal returns (ISignatureGateway.Repay memory) { + ) internal returns (ISignatureGateway.RepayAction memory) { return - ISignatureGateway.Repay({ - spoke: address(spoke), - reserveId: _randomReserveId(spoke), - amount: vm.randomUint(1, MAX_SUPPLY_AMOUNT), + ISignatureGateway.RepayAction({ onBehalfOf: who, nonce: gateway.nonces(who, _randomNonceKey()), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke), + reserveId: _randomReserveId(spoke), + amount: vm.randomUint(1, MAX_SUPPLY_AMOUNT) + }) }); } - function _setAsCollateralData( + function _setAsCollateralAction( ISpoke spoke, address who, uint256 deadline - ) internal returns (ISignatureGateway.SetUsingAsCollateral memory) { + ) internal returns (ISignatureGateway.SetUsingAsCollateralAction memory) { return - ISignatureGateway.SetUsingAsCollateral({ - spoke: address(spoke), - reserveId: _randomReserveId(spoke), - useAsCollateral: vm.randomBool(), + ISignatureGateway.SetUsingAsCollateralAction({ onBehalfOf: who, nonce: gateway.nonces(who, _randomNonceKey()), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.SetUsingAsCollateralParams({ + spoke: address(spoke), + reserveId: _randomReserveId(spoke), + useAsCollateral: vm.randomBool() + }) }); } - function _updateRiskPremiumData( + function _updateRiskPremiumAction( ISpoke spoke, address user, uint256 deadline - ) internal returns (ISignatureGateway.UpdateUserRiskPremium memory) { + ) internal returns (ISignatureGateway.UpdateUserRiskPremiumAction memory) { return - ISignatureGateway.UpdateUserRiskPremium({ - spoke: address(spoke), + ISignatureGateway.UpdateUserRiskPremiumAction({ user: user, nonce: gateway.nonces(user, _randomNonceKey()), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.UpdateUserRiskPremiumParams({spoke: address(spoke)}) }); } - function _updateDynamicConfigData( + function _updateDynamicConfigAction( ISpoke spoke, address user, uint256 deadline - ) internal returns (ISignatureGateway.UpdateUserDynamicConfig memory) { + ) internal returns (ISignatureGateway.UpdateUserDynamicConfigAction memory) { return - ISignatureGateway.UpdateUserDynamicConfig({ - spoke: address(spoke), + ISignatureGateway.UpdateUserDynamicConfigAction({ user: user, nonce: gateway.nonces(user, _randomNonceKey()), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.UpdateUserDynamicConfigParams({spoke: address(spoke)}) }); } function _getTypedDataHash( ISignatureGateway _gateway, - ISignatureGateway.Supply memory _params + ISignatureGateway.SupplyAction memory _action ) internal view returns (bytes32) { - return _typedDataHash(_gateway, vm.eip712HashStruct('Supply', abi.encode(_params))); + return _typedDataHash(_gateway, vm.eip712HashStruct('SupplyAction', abi.encode(_action))); } function _getTypedDataHash( ISignatureGateway _gateway, - ISignatureGateway.Withdraw memory _params + ISignatureGateway.WithdrawAction memory _action ) internal view returns (bytes32) { - return _typedDataHash(_gateway, vm.eip712HashStruct('Withdraw', abi.encode(_params))); + return _typedDataHash(_gateway, vm.eip712HashStruct('WithdrawAction', abi.encode(_action))); } function _getTypedDataHash( ISignatureGateway _gateway, - ISignatureGateway.Borrow memory _params + ISignatureGateway.BorrowAction memory _action ) internal view returns (bytes32) { - return _typedDataHash(_gateway, vm.eip712HashStruct('Borrow', abi.encode(_params))); + return _typedDataHash(_gateway, vm.eip712HashStruct('BorrowAction', abi.encode(_action))); } function _getTypedDataHash( ISignatureGateway _gateway, - ISignatureGateway.Repay memory _params + ISignatureGateway.RepayAction memory _action ) internal view returns (bytes32) { - return _typedDataHash(_gateway, vm.eip712HashStruct('Repay', abi.encode(_params))); + return _typedDataHash(_gateway, vm.eip712HashStruct('RepayAction', abi.encode(_action))); } function _getTypedDataHash( ISignatureGateway _gateway, - ISignatureGateway.SetUsingAsCollateral memory _params + ISignatureGateway.SetUsingAsCollateralAction memory _action ) internal view returns (bytes32) { return - _typedDataHash(_gateway, vm.eip712HashStruct('SetUsingAsCollateral', abi.encode(_params))); + _typedDataHash( + _gateway, + vm.eip712HashStruct('SetUsingAsCollateralAction', abi.encode(_action)) + ); } function _getTypedDataHash( ISignatureGateway _gateway, - ISignatureGateway.UpdateUserRiskPremium memory _params + ISignatureGateway.UpdateUserRiskPremiumAction memory _action ) internal view returns (bytes32) { return - _typedDataHash(_gateway, vm.eip712HashStruct('UpdateUserRiskPremium', abi.encode(_params))); + _typedDataHash( + _gateway, + vm.eip712HashStruct('UpdateUserRiskPremiumAction', abi.encode(_action)) + ); } function _getTypedDataHash( ISignatureGateway _gateway, - ISignatureGateway.UpdateUserDynamicConfig memory _params + ISignatureGateway.UpdateUserDynamicConfigAction memory _action ) internal view returns (bytes32) { return - _typedDataHash(_gateway, vm.eip712HashStruct('UpdateUserDynamicConfig', abi.encode(_params))); + _typedDataHash( + _gateway, + vm.eip712HashStruct('UpdateUserDynamicConfigAction', abi.encode(_action)) + ); } function _typedDataHash( @@ -215,4 +234,189 @@ contract SignatureGatewayBaseTest is SpokeBase { assertFalse(_isBorrowing(spoke, reserveId, address(_gateway))); } } + + // ============ Batch Helpers ============ + + function _supplyParams( + ISpoke spoke, + uint256 reserveId, + uint256 amount + ) internal pure returns (ISignatureGateway.SupplyParams memory) { + return + ISignatureGateway.SupplyParams({spoke: address(spoke), reserveId: reserveId, amount: amount}); + } + + function _withdrawParams( + ISpoke spoke, + uint256 reserveId, + uint256 amount + ) internal pure returns (ISignatureGateway.WithdrawParams memory) { + return + ISignatureGateway.WithdrawParams({ + spoke: address(spoke), + reserveId: reserveId, + amount: amount + }); + } + + function _borrowParams( + ISpoke spoke, + uint256 reserveId, + uint256 amount + ) internal pure returns (ISignatureGateway.BorrowParams memory) { + return + ISignatureGateway.BorrowParams({spoke: address(spoke), reserveId: reserveId, amount: amount}); + } + + function _repayParams( + ISpoke spoke, + uint256 reserveId, + uint256 amount + ) internal pure returns (ISignatureGateway.RepayParams memory) { + return + ISignatureGateway.RepayParams({spoke: address(spoke), reserveId: reserveId, amount: amount}); + } + + function _setUsingAsCollateralParams( + ISpoke spoke, + uint256 reserveId, + bool useAsCollateral + ) internal pure returns (ISignatureGateway.SetUsingAsCollateralParams memory) { + return + ISignatureGateway.SetUsingAsCollateralParams({ + spoke: address(spoke), + reserveId: reserveId, + useAsCollateral: useAsCollateral + }); + } + + function _getBatchTypedDataHash( + ISignatureGateway _gateway, + uint8[] memory actionTypes, + bytes[] memory actionData, + address onBehalfOf, + uint256 nonce, + uint256 deadline + ) internal view returns (bytes32) { + bytes32 typeHash = keccak256(bytes(_buildBatchTypeString(actionTypes))); + + bytes memory encoded = abi.encode(typeHash); + for (uint256 i = 0; i < actionTypes.length; i++) { + bytes32 actionHash = _hashActionParams(actionTypes[i], actionData[i]); + encoded = bytes.concat(encoded, abi.encode(actionHash)); + } + encoded = bytes.concat(encoded, abi.encode(onBehalfOf, nonce, deadline)); + + bytes32 structHash = keccak256(encoded); + + return keccak256(abi.encodePacked('\x19\x01', _gateway.DOMAIN_SEPARATOR(), structHash)); + } + + function _buildBatchTypeString(uint8[] memory actionTypes) internal pure returns (string memory) { + uint256 len = actionTypes.length; + uint8 usedTypes = 0; + + bytes memory batchPart = 'Batch('; + + for (uint256 i = 0; i < len; i++) { + uint8 actionType = actionTypes[i]; + usedTypes |= uint8(1 << actionType); + batchPart = bytes.concat( + batchPart, + bytes(_getParamsTypeName(actionType)), + ' action', + bytes(_uintToString(i)), + ',' + ); + } + + batchPart = bytes.concat(batchPart, 'address onBehalfOf,uint256 nonce,uint256 deadline)'); + + bytes memory typeDefs = ''; + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Borrow)) != 0) { + typeDefs = bytes.concat( + typeDefs, + 'BorrowParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Repay)) != 0) { + typeDefs = bytes.concat( + typeDefs, + 'RepayParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) != 0) { + typeDefs = bytes.concat( + typeDefs, + 'SetUsingAsCollateralParams(address spoke,uint256 reserveId,bool useAsCollateral)' + ); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Supply)) != 0) { + typeDefs = bytes.concat( + typeDefs, + 'SupplyParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) != 0) { + typeDefs = bytes.concat(typeDefs, 'UpdateUserDynamicConfigParams(address spoke)'); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) != 0) { + typeDefs = bytes.concat(typeDefs, 'UpdateUserRiskPremiumParams(address spoke)'); + } + if (usedTypes & (1 << uint8(ISignatureGateway.ActionType.Withdraw)) != 0) { + typeDefs = bytes.concat( + typeDefs, + 'WithdrawParams(address spoke,uint256 reserveId,uint256 amount)' + ); + } + + return string(bytes.concat(batchPart, typeDefs)); + } + + function _hashActionParams( + uint8 actionType, + bytes memory actionData + ) internal pure returns (bytes32) { + if (actionType == uint8(ISignatureGateway.ActionType.Supply)) { + return vm.eip712HashStruct('SupplyParams', actionData); + } else if (actionType == uint8(ISignatureGateway.ActionType.Withdraw)) { + return vm.eip712HashStruct('WithdrawParams', actionData); + } else if (actionType == uint8(ISignatureGateway.ActionType.Borrow)) { + return vm.eip712HashStruct('BorrowParams', actionData); + } else if (actionType == uint8(ISignatureGateway.ActionType.Repay)) { + return vm.eip712HashStruct('RepayParams', actionData); + } else if (actionType == uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) { + return vm.eip712HashStruct('SetUsingAsCollateralParams', actionData); + } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) { + return vm.eip712HashStruct('UpdateUserRiskPremiumParams', actionData); + } else if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) { + return vm.eip712HashStruct('UpdateUserDynamicConfigParams', actionData); + } else { + revert('Invalid action type'); + } + } + + function _getParamsTypeName(uint8 actionType) internal pure returns (string memory) { + if (actionType == uint8(ISignatureGateway.ActionType.Supply)) return 'SupplyParams'; + if (actionType == uint8(ISignatureGateway.ActionType.Withdraw)) return 'WithdrawParams'; + if (actionType == uint8(ISignatureGateway.ActionType.Borrow)) return 'BorrowParams'; + if (actionType == uint8(ISignatureGateway.ActionType.Repay)) return 'RepayParams'; + if (actionType == uint8(ISignatureGateway.ActionType.SetUsingAsCollateral)) + return 'SetUsingAsCollateralParams'; + if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium)) + return 'UpdateUserRiskPremiumParams'; + if (actionType == uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig)) + return 'UpdateUserDynamicConfigParams'; + revert('Invalid action type'); + } + + function _uintToString(uint256 value) internal pure returns (string memory) { + if (value < 10) return string(abi.encodePacked(bytes1(uint8(48 + value)))); + bytes memory buffer; + while (value > 0) { + buffer = bytes.concat(bytes1(uint8(48 + (value % 10))), buffer); + value /= 10; + } + return string(buffer); + } } diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Batch.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Batch.t.sol index 017340de2..cb414ba3a 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Batch.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Batch.t.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import 'tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol'; -import {BatchEIP712} from 'src/position-manager/libraries/BatchEIP712.sol'; import {ISpokeBase} from 'src/spoke/interfaces/ISpokeBase.sol'; contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { @@ -20,77 +19,6 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { assertTrue(spoke1.isPositionManager(alice, address(gateway))); } - function _supplyActionData( - ISpoke spoke, - uint256 reserveId, - uint256 amount - ) internal view returns (ISignatureGateway.SupplyAction memory) { - return - ISignatureGateway.SupplyAction({spoke: address(spoke), reserveId: reserveId, amount: amount}); - } - - function _withdrawActionData( - ISpoke spoke, - uint256 reserveId, - uint256 amount - ) internal view returns (ISignatureGateway.WithdrawAction memory) { - return - ISignatureGateway.WithdrawAction({ - spoke: address(spoke), - reserveId: reserveId, - amount: amount - }); - } - - function _borrowActionData( - ISpoke spoke, - uint256 reserveId, - uint256 amount - ) internal view returns (ISignatureGateway.BorrowAction memory) { - return - ISignatureGateway.BorrowAction({spoke: address(spoke), reserveId: reserveId, amount: amount}); - } - - function _repayActionData( - ISpoke spoke, - uint256 reserveId, - uint256 amount - ) internal view returns (ISignatureGateway.RepayAction memory) { - return - ISignatureGateway.RepayAction({spoke: address(spoke), reserveId: reserveId, amount: amount}); - } - - function _setUsingAsCollateralActionData( - ISpoke spoke, - uint256 reserveId, - bool useAsCollateral - ) internal view returns (ISignatureGateway.SetUsingAsCollateralAction memory) { - return - ISignatureGateway.SetUsingAsCollateralAction({ - spoke: address(spoke), - reserveId: reserveId, - useAsCollateral: useAsCollateral - }); - } - - function _getBatchTypedDataHash( - ISignatureGateway _gateway, - uint8[] memory actionTypes, - bytes[] memory actionData, - address onBehalfOf, - uint256 nonce, - uint256 deadline - ) internal view returns (bytes32) { - bytes32 structHash = BatchEIP712.hashBatch( - actionTypes, - actionData, - onBehalfOf, - nonce, - deadline - ); - return keccak256(abi.encodePacked('\x19\x01', _gateway.DOMAIN_SEPARATOR(), structHash)); - } - function test_executeBatchWithSig_singleSupply() public { uint256 deadline = _warpBeforeRandomDeadline(); uint256 reserveId = _randomReserveId(spoke1); @@ -101,7 +29,7 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); bytes[] memory actionData = new bytes[](1); - actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, amount)); + actionData[0] = abi.encode(_supplyParams(spoke1, reserveId, amount)); bytes32 digest = _getBatchTypedDataHash( gateway, @@ -138,8 +66,8 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); bytes[] memory actionData = new bytes[](2); - actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, supplyAmount)); - actionData[1] = abi.encode(_withdrawActionData(spoke1, reserveId, withdrawAmount)); + actionData[0] = abi.encode(_supplyParams(spoke1, reserveId, supplyAmount)); + actionData[1] = abi.encode(_withdrawParams(spoke1, reserveId, withdrawAmount)); bytes32 digest = _getBatchTypedDataHash( gateway, @@ -180,8 +108,8 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { actionTypes[1] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); bytes[] memory actionData = new bytes[](2); - actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, amount)); - actionData[1] = abi.encode(_setUsingAsCollateralActionData(spoke1, reserveId, true)); + actionData[0] = abi.encode(_supplyParams(spoke1, reserveId, amount)); + actionData[1] = abi.encode(_setUsingAsCollateralParams(spoke1, reserveId, true)); bytes32 digest = _getBatchTypedDataHash( gateway, @@ -224,8 +152,8 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { actionTypes[1] = uint8(ISignatureGateway.ActionType.Repay); bytes[] memory actionData = new bytes[](2); - actionData[0] = abi.encode(_borrowActionData(spoke1, reserveId, borrowAmount)); - actionData[1] = abi.encode(_repayActionData(spoke1, reserveId, repayAmount)); + actionData[0] = abi.encode(_borrowParams(spoke1, reserveId, borrowAmount)); + actionData[1] = abi.encode(_repayParams(spoke1, reserveId, repayAmount)); bytes32 digest = _getBatchTypedDataHash( gateway, @@ -265,7 +193,7 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); bytes[] memory actionData = new bytes[](1); - actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, amount)); + actionData[0] = abi.encode(_supplyParams(spoke1, reserveId, amount)); bytes32 digest = _getBatchTypedDataHash( gateway, @@ -292,7 +220,7 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); bytes[] memory actionData = new bytes[](1); - actionData[0] = abi.encode(_supplyActionData(spoke1, reserveId, amount)); + actionData[0] = abi.encode(_supplyParams(spoke1, reserveId, amount)); // Sign with wrong key (, uint256 wrongPk) = makeAddrAndKey('wrong'); @@ -320,7 +248,7 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); bytes[] memory actionData = new bytes[](1); // Mismatch! - actionData[0] = abi.encode(_supplyActionData(spoke1, 0, 1e18)); + actionData[0] = abi.encode(_supplyParams(spoke1, 0, 1e18)); // Don't compute digest - length mismatch should revert before signature verification vm.expectRevert(IGatewayBase.LengthMismatch.selector); @@ -358,67 +286,10 @@ contract SignatureGatewayBatchTest is SignatureGatewayBaseTest { actionTypes[0] = 255; // Invalid action type bytes[] memory actionData = new bytes[](1); - actionData[0] = abi.encode(_supplyActionData(spoke1, 0, 1e18)); + actionData[0] = abi.encode(_supplyParams(spoke1, 0, 1e18)); - // Note: We can't easily create a valid signature for invalid action type - // because the type string construction will fail vm.expectRevert(); vm.prank(vm.randomAddress()); gateway.executeBatchWithSig(actionTypes, actionData, alice, nonce, deadline, hex'00'); } - - function test_buildBatchTypeString_singleSupply() public pure { - uint8[] memory actionTypes = new uint8[](1); - actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); - - string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); - assertEq( - typeString, - 'Batch(SupplyAction action0,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyAction(address spoke,uint256 reserveId,uint256 amount)' - ); - } - - function test_buildBatchTypeString_supplyWithdraw() public pure { - uint8[] memory actionTypes = new uint8[](2); - actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); - actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); - - string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); - assertEq( - typeString, - 'Batch(SupplyAction action0,WithdrawAction action1,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyAction(address spoke,uint256 reserveId,uint256 amount)WithdrawAction(address spoke,uint256 reserveId,uint256 amount)' - ); - } - - function test_buildBatchTypeString_duplicateActions() public pure { - uint8[] memory actionTypes = new uint8[](3); - actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); - actionTypes[1] = uint8(ISignatureGateway.ActionType.Supply); - actionTypes[2] = uint8(ISignatureGateway.ActionType.Withdraw); - - string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); - // SupplyAction definition should only appear once - assertEq( - typeString, - 'Batch(SupplyAction action0,SupplyAction action1,WithdrawAction action2,address onBehalfOf,uint256 nonce,uint256 deadline)SupplyAction(address spoke,uint256 reserveId,uint256 amount)WithdrawAction(address spoke,uint256 reserveId,uint256 amount)' - ); - } - - function test_buildBatchTypeString_allActions() public pure { - uint8[] memory actionTypes = new uint8[](7); - actionTypes[0] = uint8(ISignatureGateway.ActionType.Supply); - actionTypes[1] = uint8(ISignatureGateway.ActionType.Withdraw); - actionTypes[2] = uint8(ISignatureGateway.ActionType.Borrow); - actionTypes[3] = uint8(ISignatureGateway.ActionType.Repay); - actionTypes[4] = uint8(ISignatureGateway.ActionType.SetUsingAsCollateral); - actionTypes[5] = uint8(ISignatureGateway.ActionType.UpdateUserRiskPremium); - actionTypes[6] = uint8(ISignatureGateway.ActionType.UpdateUserDynamicConfig); - - string memory typeString = BatchEIP712.buildBatchTypeString(actionTypes); - - // Type definitions should be in alphabetical order - string - memory expected = 'Batch(SupplyAction action0,WithdrawAction action1,BorrowAction action2,RepayAction action3,SetUsingAsCollateralAction action4,UpdateUserRiskPremiumAction action5,UpdateUserDynamicConfigAction action6,address onBehalfOf,uint256 nonce,uint256 deadline)BorrowAction(address spoke,uint256 reserveId,uint256 amount)RepayAction(address spoke,uint256 reserveId,uint256 amount)SetUsingAsCollateralAction(address spoke,uint256 reserveId,bool useAsCollateral)SupplyAction(address spoke,uint256 reserveId,uint256 amount)UpdateUserDynamicConfigAction(address spoke)UpdateUserRiskPremiumAction(address spoke)WithdrawAction(address spoke,uint256 reserveId,uint256 amount)'; - assertEq(typeString, expected); - } } diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Constants.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Constants.t.sol index 82109dfd4..1f93c8b66 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Constants.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Constants.t.sol @@ -62,76 +62,39 @@ contract SignatureGatewayConstantsTest is SignatureGatewayBaseTest { } function test_supply_typeHash() public view { - assertEq(gateway.SUPPLY_TYPEHASH(), vm.eip712HashType('Supply')); - assertEq( - gateway.SUPPLY_TYPEHASH(), - keccak256( - 'Supply(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) - ); + assertEq(gateway.SUPPLY_TYPEHASH(), vm.eip712HashType('SupplyAction')); } function test_withdraw_typeHash() public view { - assertEq(gateway.WITHDRAW_TYPEHASH(), vm.eip712HashType('Withdraw')); - assertEq( - gateway.WITHDRAW_TYPEHASH(), - keccak256( - 'Withdraw(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) - ); + assertEq(gateway.WITHDRAW_TYPEHASH(), vm.eip712HashType('WithdrawAction')); } function test_borrow_typeHash() public view { - assertEq(gateway.BORROW_TYPEHASH(), vm.eip712HashType('Borrow')); - assertEq( - gateway.BORROW_TYPEHASH(), - keccak256( - 'Borrow(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) - ); + assertEq(gateway.BORROW_TYPEHASH(), vm.eip712HashType('BorrowAction')); } function test_repay_typeHash() public view { - assertEq(gateway.REPAY_TYPEHASH(), vm.eip712HashType('Repay')); - assertEq( - gateway.REPAY_TYPEHASH(), - keccak256( - 'Repay(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) - ); + assertEq(gateway.REPAY_TYPEHASH(), vm.eip712HashType('RepayAction')); } function test_setUsingAsCollateral_typeHash() public view { - assertEq(gateway.SET_USING_AS_COLLATERAL_TYPEHASH(), vm.eip712HashType('SetUsingAsCollateral')); assertEq( gateway.SET_USING_AS_COLLATERAL_TYPEHASH(), - keccak256( - 'SetUsingAsCollateral(address spoke,uint256 reserveId,bool useAsCollateral,address onBehalfOf,uint256 nonce,uint256 deadline)' - ) + vm.eip712HashType('SetUsingAsCollateralAction') ); } function test_updateUserRiskPremium_typeHash() public view { assertEq( gateway.UPDATE_USER_RISK_PREMIUM_TYPEHASH(), - vm.eip712HashType('UpdateUserRiskPremium') - ); - assertEq( - gateway.UPDATE_USER_RISK_PREMIUM_TYPEHASH(), - keccak256('UpdateUserRiskPremium(address spoke,address user,uint256 nonce,uint256 deadline)') + vm.eip712HashType('UpdateUserRiskPremiumAction') ); } function test_updateUserDynamicConfig_typeHash() public view { assertEq( gateway.UPDATE_USER_DYNAMIC_CONFIG_TYPEHASH(), - vm.eip712HashType('UpdateUserDynamicConfig') - ); - assertEq( - gateway.UPDATE_USER_DYNAMIC_CONFIG_TYPEHASH(), - keccak256( - 'UpdateUserDynamicConfig(address spoke,address user,uint256 nonce,uint256 deadline)' - ) + vm.eip712HashType('UpdateUserDynamicConfigAction') ); } } diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Base.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Base.t.sol index 0fcb21f2a..78510d8b9 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Base.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Base.t.sol @@ -5,22 +5,9 @@ pragma solidity ^0.8.0; import 'tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol'; import {ISignatureTransfer} from 'lib/permit2/src/interfaces/ISignatureTransfer.sol'; import {DeployPermit2} from 'lib/permit2/test/utils/DeployPermit2.sol'; -import {EIP712Hash} from 'src/position-manager/libraries/EIP712Hash.sol'; +import {EIP712Types} from 'tests/mocks/EIP712Types.sol'; contract SignatureGatewayPermit2BaseTest is SignatureGatewayBaseTest, DeployPermit2 { - bytes32 internal constant _TOKEN_PERMISSIONS_TYPEHASH = - keccak256('TokenPermissions(address token,uint256 amount)'); - - bytes32 internal constant _FULL_SUPPLY_WITNESS_TYPEHASH = - keccak256( - 'PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,Supply witness)Supply(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)' - ); - - bytes32 internal constant _FULL_REPAY_WITNESS_TYPEHASH = - keccak256( - 'PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,Repay witness)Repay(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)' - ); - bytes32 internal PERMIT2_DOMAIN_SEPARATOR; function setUp() public virtual override { @@ -49,7 +36,7 @@ contract SignatureGatewayPermit2BaseTest is SignatureGatewayBaseTest, DeployPerm internal returns ( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Supply memory params + ISignatureGateway.SupplyAction memory params ) { uint256 reserveId = _randomReserveId(spoke); @@ -63,13 +50,15 @@ contract SignatureGatewayPermit2BaseTest is SignatureGatewayBaseTest, DeployPerm deadline: deadline }); - params = ISignatureGateway.Supply({ - spoke: address(spoke), - reserveId: reserveId, - amount: amount, + params = ISignatureGateway.SupplyAction({ onBehalfOf: who, nonce: nonce, - deadline: deadline + deadline: deadline, + params: ISignatureGateway.SupplyParams({ + spoke: address(spoke), + reserveId: reserveId, + amount: amount + }) }); } @@ -81,7 +70,7 @@ contract SignatureGatewayPermit2BaseTest is SignatureGatewayBaseTest, DeployPerm internal returns ( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Repay memory params + ISignatureGateway.RepayAction memory params ) { uint256 reserveId = _randomReserveId(spoke); @@ -95,70 +84,102 @@ contract SignatureGatewayPermit2BaseTest is SignatureGatewayBaseTest, DeployPerm deadline: deadline }); - params = ISignatureGateway.Repay({ - spoke: address(spoke), - reserveId: reserveId, - amount: amount, + params = ISignatureGateway.RepayAction({ onBehalfOf: who, nonce: nonce, - deadline: deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke), + reserveId: reserveId, + amount: amount + }) }); } function _getPermit2SupplySignature( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Supply memory params, + ISignatureGateway.SupplyAction memory action, uint256 privateKey ) internal view returns (bytes memory) { - bytes32 witness = _getSupplyWitnessHash(params); - return _getPermit2WitnessSignature(permit, witness, _FULL_SUPPLY_WITNESS_TYPEHASH, privateKey); + return _getPermit2SupplySignatureForSpender(permit, action, privateKey, address(gateway)); } - function _getPermit2RepaySignature( + function _getPermit2SupplySignatureForSpender( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Repay memory params, - uint256 privateKey + ISignatureGateway.SupplyAction memory action, + uint256 privateKey, + address spender ) internal view returns (bytes memory) { - bytes32 witness = _getRepayWitnessHash(params); - return _getPermit2WitnessSignature(permit, witness, _FULL_REPAY_WITNESS_TYPEHASH, privateKey); + EIP712Types.PermitWitnessTransferFromSupplyAction memory permitWitness = EIP712Types + .PermitWitnessTransferFromSupplyAction({ + permitted: EIP712Types.TokenPermissions({ + token: permit.permitted.token, + amount: permit.permitted.amount + }), + spender: spender, + nonce: permit.nonce, + deadline: permit.deadline, + witness: EIP712Types.SupplyAction({ + onBehalfOf: action.onBehalfOf, + nonce: action.nonce, + deadline: action.deadline, + params: EIP712Types.SupplyParams({ + spoke: action.params.spoke, + reserveId: action.params.reserveId, + amount: action.params.amount + }) + }) + }); + + bytes32 structHash = vm.eip712HashStruct( + 'PermitWitnessTransferFromSupplyAction', + abi.encode(permitWitness) + ); + + bytes32 digest = keccak256(abi.encodePacked('\x19\x01', PERMIT2_DOMAIN_SEPARATOR, structHash)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); } - function _getPermit2WitnessSignature( + function _getPermit2RepaySignature( ISignatureTransfer.PermitTransferFrom memory permit, - bytes32 witness, - bytes32 fullTypehash, + ISignatureGateway.RepayAction memory action, uint256 privateKey ) internal view returns (bytes memory) { - return - _getPermit2WitnessSignatureForSpender( - permit, - witness, - fullTypehash, - privateKey, - address(gateway) - ); + return _getPermit2RepaySignatureForSpender(permit, action, privateKey, address(gateway)); } - function _getPermit2WitnessSignatureForSpender( + function _getPermit2RepaySignatureForSpender( ISignatureTransfer.PermitTransferFrom memory permit, - bytes32 witness, - bytes32 fullTypehash, + ISignatureGateway.RepayAction memory action, uint256 privateKey, address spender ) internal view returns (bytes memory) { - bytes32 tokenPermissionsHash = keccak256( - abi.encode(_TOKEN_PERMISSIONS_TYPEHASH, permit.permitted.token, permit.permitted.amount) - ); + EIP712Types.PermitWitnessTransferFromRepayAction memory permitWitness = EIP712Types + .PermitWitnessTransferFromRepayAction({ + permitted: EIP712Types.TokenPermissions({ + token: permit.permitted.token, + amount: permit.permitted.amount + }), + spender: spender, + nonce: permit.nonce, + deadline: permit.deadline, + witness: EIP712Types.RepayAction({ + onBehalfOf: action.onBehalfOf, + nonce: action.nonce, + deadline: action.deadline, + params: EIP712Types.RepayParams({ + spoke: action.params.spoke, + reserveId: action.params.reserveId, + amount: action.params.amount + }) + }) + }); - bytes32 structHash = keccak256( - abi.encode( - fullTypehash, - tokenPermissionsHash, - spender, - permit.nonce, - permit.deadline, - witness - ) + bytes32 structHash = vm.eip712HashStruct( + 'PermitWitnessTransferFromRepayAction', + abi.encode(permitWitness) ); bytes32 digest = keccak256(abi.encodePacked('\x19\x01', PERMIT2_DOMAIN_SEPARATOR, structHash)); @@ -168,37 +189,15 @@ contract SignatureGatewayPermit2BaseTest is SignatureGatewayBaseTest, DeployPerm } function _getSupplyWitnessHash( - ISignatureGateway.Supply memory params + ISignatureGateway.SupplyAction memory action ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - EIP712Hash.SUPPLY_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline - ) - ); + return vm.eip712HashStruct('SupplyAction', abi.encode(action)); } function _getRepayWitnessHash( - ISignatureGateway.Repay memory params + ISignatureGateway.RepayAction memory action ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - EIP712Hash.REPAY_TYPEHASH, - params.spoke, - params.reserveId, - params.amount, - params.onBehalfOf, - params.nonce, - params.deadline - ) - ); + return vm.eip712HashStruct('RepayAction', abi.encode(action)); } function _approvePermit2(ISpoke spoke, uint256 reserveId, address who) internal { diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Reverts.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Reverts.t.sol index 377ab1e54..1723fa635 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Reverts.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.Reverts.t.sol @@ -15,14 +15,14 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { function test_supplyWithPermit2_revertsWith_InvalidSigner() public { ( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Supply memory p + ISignatureGateway.SupplyAction memory p ) = _permit2SupplyData(spoke1, alice, _warpBeforeRandomDeadline()); (, uint256 bobPk) = makeAddrAndKey('bob'); bytes memory signature = _getPermit2SupplySignature(permit, p, bobPk); - _approvePermit2(spoke1, p.reserveId, alice); - deal(permit.permitted.token, alice, p.amount); + _approvePermit2(spoke1, p.params.reserveId, alice); + deal(permit.permitted.token, alice, p.params.amount); vm.expectRevert(InvalidSigner.selector); vm.prank(vm.randomAddress()); @@ -45,19 +45,21 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { deadline: expiredDeadline // Permit2 deadline is expired }); - ISignatureGateway.Supply memory p = ISignatureGateway.Supply({ - spoke: address(spoke1), - reserveId: reserveId, - amount: amount, + ISignatureGateway.SupplyAction memory p = ISignatureGateway.SupplyAction({ onBehalfOf: alice, nonce: nonce, - deadline: validDeadline // Gateway deadline is valid + deadline: validDeadline, // Gateway deadline is valid + params: ISignatureGateway.SupplyParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount + }) }); bytes memory signature = _getPermit2SupplySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); - deal(permit.permitted.token, alice, p.amount); + _approvePermit2(spoke1, p.params.reserveId, alice); + deal(permit.permitted.token, alice, p.params.amount); vm.expectRevert(abi.encodeWithSelector(SignatureExpired.selector, permit.deadline)); vm.prank(vm.randomAddress()); @@ -66,20 +68,19 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { function test_supplyWithPermit2_revertsWith_SpokeNotRegistered() public { address unregisteredSpoke = vm.randomAddress(); + uint256 deadline = _warpBeforeRandomDeadline(); ISignatureTransfer.PermitTransferFrom memory permit = ISignatureTransfer.PermitTransferFrom({ permitted: ISignatureTransfer.TokenPermissions({token: address(0), amount: 1e18}), nonce: _randomUnusedPermit2Nonce(alice), - deadline: _warpBeforeRandomDeadline() + deadline: deadline }); - ISignatureGateway.Supply memory p = ISignatureGateway.Supply({ - spoke: unregisteredSpoke, - reserveId: 0, - amount: 1e18, + ISignatureGateway.SupplyAction memory p = ISignatureGateway.SupplyAction({ onBehalfOf: alice, nonce: permit.nonce, - deadline: permit.deadline + deadline: deadline, + params: ISignatureGateway.SupplyParams({spoke: unregisteredSpoke, reserveId: 0, amount: 1e18}) }); bytes memory signature = _getPermit2SupplySignature(permit, p, alicePk); @@ -110,19 +111,21 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { deadline: deadline }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: borrowAmount, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: nonce, - deadline: deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: borrowAmount + }) }); (, uint256 bobPk) = makeAddrAndKey('bob'); bytes memory signature = _getPermit2RepaySignature(permit, p, bobPk); - _approvePermit2(spoke1, p.reserveId, alice); + _approvePermit2(spoke1, p.params.reserveId, alice); vm.expectRevert(InvalidSigner.selector); vm.prank(vm.randomAddress()); @@ -150,18 +153,20 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { deadline: expiredDeadline // Permit2 deadline is expired }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: borrowAmount, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: nonce, - deadline: validDeadline // Gateway deadline is valid + deadline: validDeadline, // Gateway deadline is valid + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: borrowAmount + }) }); bytes memory signature = _getPermit2RepaySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); + _approvePermit2(spoke1, p.params.reserveId, alice); vm.expectRevert(abi.encodeWithSelector(SignatureExpired.selector, permit.deadline)); vm.prank(vm.randomAddress()); @@ -170,20 +175,19 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { function test_repayWithPermit2_revertsWith_SpokeNotRegistered() public { address unregisteredSpoke = vm.randomAddress(); + uint256 deadline = _warpBeforeRandomDeadline(); ISignatureTransfer.PermitTransferFrom memory permit = ISignatureTransfer.PermitTransferFrom({ permitted: ISignatureTransfer.TokenPermissions({token: address(0), amount: 1e18}), nonce: _randomUnusedPermit2Nonce(alice), - deadline: _warpBeforeRandomDeadline() + deadline: deadline }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: unregisteredSpoke, - reserveId: 0, - amount: 1e18, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: permit.nonce, - deadline: permit.deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({spoke: unregisteredSpoke, reserveId: 0, amount: 1e18}) }); bytes memory signature = _getPermit2RepaySignature(permit, p, alicePk); @@ -204,7 +208,7 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { ( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Supply memory p + ISignatureGateway.SupplyAction memory p ) = _permit2SupplyData(spoke1, alice, deadline); // Set permit deadline to far future so Permit2 won't reject it @@ -212,8 +216,8 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { bytes memory signature = _getPermit2SupplySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); - deal(permit.permitted.token, alice, p.amount); + _approvePermit2(spoke1, p.params.reserveId, alice); + deal(permit.permitted.token, alice, p.params.amount); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); @@ -241,18 +245,20 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { deadline: type(uint256).max // Set permit deadline to far future }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: borrowAmount, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: nonce, - deadline: deadline // Gateway deadline is expired + deadline: deadline, // Gateway deadline is expired + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: borrowAmount + }) }); bytes memory signature = _getPermit2RepaySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); + _approvePermit2(spoke1, p.params.reserveId, alice); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); vm.prank(vm.randomAddress()); @@ -276,19 +282,21 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { deadline: deadline }); - ISignatureGateway.Supply memory p = ISignatureGateway.Supply({ - spoke: address(spoke1), - reserveId: reserveId, - amount: amount, + ISignatureGateway.SupplyAction memory p = ISignatureGateway.SupplyAction({ onBehalfOf: alice, nonce: _getRandomInvalidNonceAtKey(gateway, alice, nonceKey), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.SupplyParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: amount + }) }); bytes memory signature = _getPermit2SupplySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); - deal(permit.permitted.token, alice, p.amount); + _approvePermit2(spoke1, p.params.reserveId, alice); + deal(permit.permitted.token, alice, p.params.amount); vm.expectRevert( abi.encodeWithSelector(INoncesKeyed.InvalidAccountNonce.selector, alice, currentNonce) @@ -317,18 +325,20 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { deadline: deadline }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: borrowAmount, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: _getRandomInvalidNonceAtKey(gateway, alice, nonceKey), - deadline: deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: borrowAmount + }) }); bytes memory signature = _getPermit2RepaySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); + _approvePermit2(spoke1, p.params.reserveId, alice); vm.expectRevert( abi.encodeWithSelector(INoncesKeyed.InvalidAccountNonce.selector, alice, currentNonce) @@ -344,14 +354,14 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { function test_supplyWithPermit2_cannotBeFrontrun_signatureBoundToGatewaySpender() public { ( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Supply memory p + ISignatureGateway.SupplyAction memory p ) = _permit2SupplyData(spoke1, alice, _warpBeforeRandomDeadline()); // Alice signs for gateway as spender bytes memory signature = _getPermit2SupplySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); - deal(permit.permitted.token, alice, p.amount); + _approvePermit2(spoke1, p.params.reserveId, alice); + deal(permit.permitted.token, alice, p.params.amount); // Cache PERMIT2 address before prank (prank only applies to next external call) ISignatureTransfer permit2 = ISignatureTransfer(gateway.PERMIT2()); @@ -363,7 +373,7 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { vm.prank(attacker); permit2.permitWitnessTransferFrom( permit, - ISignatureTransfer.SignatureTransferDetails({to: attacker, requestedAmount: p.amount}), + ISignatureTransfer.SignatureTransferDetails({to: attacker, requestedAmount: p.params.amount}), alice, _getSupplyWitnessHash(p), 'Supply witness)Supply(address spoke,uint256 reserveId,uint256 amount,address onBehalfOf,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)', @@ -390,19 +400,21 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { deadline: deadline }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: borrowAmount, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: nonce, - deadline: deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: borrowAmount + }) }); // Alice signs for gateway as spender bytes memory signature = _getPermit2RepaySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); + _approvePermit2(spoke1, p.params.reserveId, alice); // Cache PERMIT2 address before prank (prank only applies to next external call) ISignatureTransfer permit2 = ISignatureTransfer(gateway.PERMIT2()); @@ -424,21 +436,20 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { function test_supplyWithPermit2_revertsWith_InvalidSigner_whenSignedForDifferentSpender() public { ( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Supply memory p + ISignatureGateway.SupplyAction memory p ) = _permit2SupplyData(spoke1, alice, _warpBeforeRandomDeadline()); // Alice signs for a DIFFERENT spender (not the gateway) address differentSpender = vm.randomAddress(); - bytes memory signature = _getPermit2WitnessSignatureForSpender( + bytes memory signature = _getPermit2SupplySignatureForSpender( permit, - _getSupplyWitnessHash(p), - _FULL_SUPPLY_WITNESS_TYPEHASH, + p, alicePk, differentSpender ); - _approvePermit2(spoke1, p.reserveId, alice); - deal(permit.permitted.token, alice, p.amount); + _approvePermit2(spoke1, p.params.reserveId, alice); + deal(permit.permitted.token, alice, p.params.amount); // Gateway call should fail because signature was signed for different spender vm.expectRevert(InvalidSigner.selector); @@ -465,26 +476,27 @@ contract SignatureGatewayPermit2RevertsTest is SignatureGatewayPermit2BaseTest { deadline: deadline }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: borrowAmount, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: nonce, - deadline: deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: borrowAmount + }) }); // Alice signs for a DIFFERENT spender (not the gateway) address differentSpender = vm.randomAddress(); - bytes memory signature = _getPermit2WitnessSignatureForSpender( + bytes memory signature = _getPermit2RepaySignatureForSpender( permit, - _getRepayWitnessHash(p), - _FULL_REPAY_WITNESS_TYPEHASH, + p, alicePk, differentSpender ); - _approvePermit2(spoke1, p.reserveId, alice); + _approvePermit2(spoke1, p.params.reserveId, alice); // Gateway call should fail because signature was signed for different spender vm.expectRevert(InvalidSigner.selector); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.t.sol index 209fb14d4..4a0750d22 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Permit2.t.sol @@ -10,28 +10,28 @@ contract SignatureGatewayPermit2Test is SignatureGatewayPermit2BaseTest { function test_supplyWithPermit2(bytes32) public { ( ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureGateway.Supply memory p + ISignatureGateway.SupplyAction memory p ) = _permit2SupplyData(spoke1, alice, _warpBeforeRandomDeadline()); bytes memory signature = _getPermit2SupplySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); - deal(permit.permitted.token, alice, p.amount); + _approvePermit2(spoke1, p.params.reserveId, alice); + deal(permit.permitted.token, alice, p.params.amount); - uint256 shares = _hub(spoke1, p.reserveId).previewAddByAssets( - _spokeAssetId(spoke1, p.reserveId), - p.amount + uint256 shares = _hub(spoke1, p.params.reserveId).previewAddByAssets( + _spokeAssetId(spoke1, p.params.reserveId), + p.params.amount ); TestReturnValues memory returnValues; vm.expectEmit(address(spoke1)); - emit ISpokeBase.Supply(p.reserveId, address(gateway), alice, shares, p.amount); + emit ISpokeBase.Supply(p.params.reserveId, address(gateway), alice, shares, p.params.amount); vm.prank(vm.randomAddress()); (returnValues.shares, returnValues.amount) = gateway.supplyWithPermit2(permit, p, signature); assertEq(returnValues.shares, shares); - assertEq(returnValues.amount, p.amount); + assertEq(returnValues.amount, p.params.amount); _assertNonceIncrement(gateway, alice, p.nonce); _assertPermit2NonceConsumed(alice, permit.nonce); @@ -61,18 +61,20 @@ contract SignatureGatewayPermit2Test is SignatureGatewayPermit2BaseTest { deadline: deadline }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: repayAmount, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: intentNonce, - deadline: deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: repayAmount + }) }); bytes memory signature = _getPermit2RepaySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); + _approvePermit2(spoke1, p.params.reserveId, alice); uint256 debtBefore = spoke1.getUserTotalDebt(reserveId, alice); @@ -114,18 +116,20 @@ contract SignatureGatewayPermit2Test is SignatureGatewayPermit2BaseTest { deadline: deadline }); - ISignatureGateway.Repay memory p = ISignatureGateway.Repay({ - spoke: address(spoke1), - reserveId: reserveId, - amount: requestedRepayAmount, + ISignatureGateway.RepayAction memory p = ISignatureGateway.RepayAction({ onBehalfOf: alice, nonce: intentNonce, - deadline: deadline + deadline: deadline, + params: ISignatureGateway.RepayParams({ + spoke: address(spoke1), + reserveId: reserveId, + amount: requestedRepayAmount + }) }); bytes memory signature = _getPermit2RepaySignature(permit, p, alicePk); - _approvePermit2(spoke1, p.reserveId, alice); + _approvePermit2(spoke1, p.params.reserveId, alice); vm.prank(vm.randomAddress()); (, uint256 amount) = gateway.repayWithPermit2(permit, p, signature); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InsufficientAllowance.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InsufficientAllowance.t.sol index ce2796ebe..1a22ce7ff 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InsufficientAllowance.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InsufficientAllowance.t.sol @@ -20,7 +20,7 @@ contract SignatureGateway_InsufficientAllowance_Test is SignatureGatewayBaseTest function test_supplyWithSig_revertsWith_ERC20InsufficientAllowance() public { uint256 deadline = _warpBeforeRandomDeadline(); - ISignatureGateway.Supply memory p = _supplyData(spoke1, alice, deadline); + ISignatureGateway.SupplyAction memory p = _supplyAction(spoke1, alice, deadline); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert( @@ -28,8 +28,8 @@ contract SignatureGateway_InsufficientAllowance_Test is SignatureGatewayBaseTest IERC20Errors.ERC20InsufficientAllowance.selector, address(gateway), 0, - p.amount, - address(_underlying(spoke1, p.reserveId)) + p.params.amount, + address(_underlying(spoke1, p.params.reserveId)) ) ); vm.prank(vm.randomAddress()); @@ -42,9 +42,9 @@ contract SignatureGateway_InsufficientAllowance_Test is SignatureGatewayBaseTest uint256 deadline = _warpBeforeRandomDeadline(); - ISignatureGateway.Repay memory p = _repayData(spoke1, alice, deadline); - p.reserveId = _daiReserveId(spoke1); - p.amount = 50e18; + ISignatureGateway.RepayAction memory p = _repayAction(spoke1, alice, deadline); + p.params.reserveId = _daiReserveId(spoke1); + p.params.amount = 50e18; bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert( @@ -52,8 +52,8 @@ contract SignatureGateway_InsufficientAllowance_Test is SignatureGatewayBaseTest IERC20Errors.ERC20InsufficientAllowance.selector, address(gateway), 0, - p.amount, - address(_underlying(spoke1, p.reserveId)) + p.params.amount, + address(_underlying(spoke1, p.params.reserveId)) ) ); vm.prank(vm.randomAddress()); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol index ce9c9ac09..16ab102d3 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.InvalidSignature.t.sol @@ -6,7 +6,11 @@ import 'tests/unit/misc/SignatureGateway/SignatureGateway.Base.t.sol'; contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { function test_supplyWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() public { - ISignatureGateway.Supply memory p = _supplyData(spoke1, alice, _warpAfterRandomDeadline()); + ISignatureGateway.SupplyAction memory p = _supplyAction( + spoke1, + alice, + _warpAfterRandomDeadline() + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); @@ -15,7 +19,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { } function test_withdrawWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() public { - ISignatureGateway.Withdraw memory p = _withdrawData(spoke1, alice, _warpAfterRandomDeadline()); + ISignatureGateway.WithdrawAction memory p = _withdrawAction( + spoke1, + alice, + _warpAfterRandomDeadline() + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); @@ -24,7 +32,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { } function test_borrowWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() public { - ISignatureGateway.Borrow memory p = _borrowData(spoke1, alice, _warpAfterRandomDeadline()); + ISignatureGateway.BorrowAction memory p = _borrowAction( + spoke1, + alice, + _warpAfterRandomDeadline() + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); @@ -33,7 +45,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { } function test_repayWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() public { - ISignatureGateway.Repay memory p = _repayData(spoke1, alice, _warpAfterRandomDeadline()); + ISignatureGateway.RepayAction memory p = _repayAction( + spoke1, + alice, + _warpAfterRandomDeadline() + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); @@ -45,7 +61,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { public { uint256 deadline = _warpAfterRandomDeadline(); - ISignatureGateway.SetUsingAsCollateral memory p = _setAsCollateralData(spoke1, alice, deadline); + ISignatureGateway.SetUsingAsCollateralAction memory p = _setAsCollateralAction( + spoke1, + alice, + deadline + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); @@ -57,7 +77,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { public { uint256 deadline = _warpAfterRandomDeadline(); - ISignatureGateway.UpdateUserRiskPremium memory p = _updateRiskPremiumData( + ISignatureGateway.UpdateUserRiskPremiumAction memory p = _updateRiskPremiumAction( spoke1, alice, deadline @@ -72,7 +92,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { function test_updateUserDynamicConfigWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() public { - ISignatureGateway.UpdateUserDynamicConfig memory p = _updateDynamicConfigData( + ISignatureGateway.UpdateUserDynamicConfigAction memory p = _updateDynamicConfigAction( spoke1, alice, _warpAfterRandomDeadline() @@ -89,7 +109,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { address onBehalfOf = vm.randomAddress(); while (onBehalfOf == randomUser) onBehalfOf = vm.randomAddress(); - ISignatureGateway.Supply memory p = _supplyData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); + ISignatureGateway.SupplyAction memory p = _supplyAction( + spoke1, + onBehalfOf, + _warpAfterRandomDeadline() + ); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); @@ -102,7 +126,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { address onBehalfOf = vm.randomAddress(); while (onBehalfOf == randomUser) onBehalfOf = vm.randomAddress(); - ISignatureGateway.Withdraw memory p = _withdrawData( + ISignatureGateway.WithdrawAction memory p = _withdrawAction( spoke1, onBehalfOf, _warpAfterRandomDeadline() @@ -119,7 +143,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { address onBehalfOf = vm.randomAddress(); while (onBehalfOf == randomUser) onBehalfOf = vm.randomAddress(); - ISignatureGateway.Borrow memory p = _borrowData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); + ISignatureGateway.BorrowAction memory p = _borrowAction( + spoke1, + onBehalfOf, + _warpAfterRandomDeadline() + ); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); @@ -132,7 +160,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { address onBehalfOf = vm.randomAddress(); while (onBehalfOf == randomUser) onBehalfOf = vm.randomAddress(); - ISignatureGateway.Repay memory p = _repayData(spoke1, onBehalfOf, _warpAfterRandomDeadline()); + ISignatureGateway.RepayAction memory p = _repayAction( + spoke1, + onBehalfOf, + _warpAfterRandomDeadline() + ); bytes memory signature = _sign(randomUserPk, _getTypedDataHash(gateway, p)); vm.expectRevert(IIntentConsumer.InvalidSignature.selector); @@ -148,7 +180,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { while (onBehalfOf == randomUser) onBehalfOf = vm.randomAddress(); uint256 deadline = _warpAfterRandomDeadline(); - ISignatureGateway.SetUsingAsCollateral memory p = _setAsCollateralData( + ISignatureGateway.SetUsingAsCollateralAction memory p = _setAsCollateralAction( spoke1, onBehalfOf, deadline @@ -168,7 +200,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { while (user == randomUser) user = vm.randomAddress(); uint256 deadline = _warpAfterRandomDeadline(); - ISignatureGateway.UpdateUserRiskPremium memory p = _updateRiskPremiumData( + ISignatureGateway.UpdateUserRiskPremiumAction memory p = _updateRiskPremiumAction( spoke1, user, deadline @@ -188,7 +220,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { while (user == randomUser) user = vm.randomAddress(); uint256 deadline = _warpAfterRandomDeadline(); - ISignatureGateway.UpdateUserDynamicConfig memory p = _updateDynamicConfigData( + ISignatureGateway.UpdateUserDynamicConfigAction memory p = _updateDynamicConfigAction( spoke1, user, deadline @@ -201,7 +233,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { } function test_supplyWithSig_revertsWith_InvalidAccountNonce(bytes32) public { - ISignatureGateway.Supply memory p = _supplyData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.SupplyAction memory p = _supplyAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); uint192 nonceKey = _randomNonceKey(); uint256 currentNonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf, nonceKey); p.nonce = _getRandomInvalidNonceAtKey(gateway, p.onBehalfOf, nonceKey); @@ -216,7 +252,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { } function test_withdrawWithSig_revertsWith_InvalidAccountNonce(bytes32) public { - ISignatureGateway.Withdraw memory p = _withdrawData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.WithdrawAction memory p = _withdrawAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); uint192 nonceKey = _randomNonceKey(); uint256 currentNonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf, nonceKey); p.nonce = _getRandomInvalidNonceAtKey(gateway, p.onBehalfOf, nonceKey); @@ -231,7 +271,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { } function test_borrowWithSig_revertsWith_InvalidAccountNonce(bytes32) public { - ISignatureGateway.Borrow memory p = _borrowData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.BorrowAction memory p = _borrowAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); uint192 nonceKey = _randomNonceKey(); uint256 currentNonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf, nonceKey); p.nonce = _getRandomInvalidNonceAtKey(gateway, p.onBehalfOf, nonceKey); @@ -246,7 +290,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { } function test_repayWithSig_revertsWith_InvalidAccountNonce(bytes32) public { - ISignatureGateway.Repay memory p = _repayData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.RepayAction memory p = _repayAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); uint192 nonceKey = _randomNonceKey(); uint256 currentNonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf, nonceKey); p.nonce = _getRandomInvalidNonceAtKey(gateway, p.onBehalfOf, nonceKey); @@ -262,7 +310,11 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { function test_setUsingAsCollateralWithSig_revertsWith_InvalidAccountNonce(bytes32) public { uint256 deadline = _warpBeforeRandomDeadline(); - ISignatureGateway.SetUsingAsCollateral memory p = _setAsCollateralData(spoke1, alice, deadline); + ISignatureGateway.SetUsingAsCollateralAction memory p = _setAsCollateralAction( + spoke1, + alice, + deadline + ); uint192 nonceKey = _randomNonceKey(); uint256 currentNonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf, nonceKey); p.nonce = _getRandomInvalidNonceAtKey(gateway, p.onBehalfOf, nonceKey); @@ -278,7 +330,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { function test_updateUserRiskPremiumWithSig_revertsWith_InvalidAccountNonce(bytes32) public { uint256 deadline = _warpBeforeRandomDeadline(); - ISignatureGateway.UpdateUserRiskPremium memory p = _updateRiskPremiumData( + ISignatureGateway.UpdateUserRiskPremiumAction memory p = _updateRiskPremiumAction( spoke1, alice, deadline @@ -297,7 +349,7 @@ contract SignatureGatewayInvalidSignatureTest is SignatureGatewayBaseTest { } function test_updateUserDynamicConfigWithSig_revertsWith_InvalidAccountNonce(bytes32) public { - ISignatureGateway.UpdateUserDynamicConfig memory p = _updateDynamicConfigData( + ISignatureGateway.UpdateUserDynamicConfigAction memory p = _updateDynamicConfigAction( spoke1, alice, _warpBeforeRandomDeadline() diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.SpokeNotRegistered.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.SpokeNotRegistered.t.sol index cd9ad6eeb..422e9bfcb 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.SpokeNotRegistered.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.SpokeNotRegistered.t.sol @@ -21,7 +21,7 @@ contract SignatureGateway_SpokeNotRegistered_Test is SignatureGatewayBaseTest { } function test_supplyWithSig_revertsWith_SpokeNotRegistered( - ISignatureGateway.Supply memory p + ISignatureGateway.SupplyAction memory p ) public { bytes memory signature = vm.randomBytes(32); @@ -31,7 +31,7 @@ contract SignatureGateway_SpokeNotRegistered_Test is SignatureGatewayBaseTest { } function test_withdrawWithSig_revertsWith_SpokeNotRegistered( - ISignatureGateway.Withdraw memory p + ISignatureGateway.WithdrawAction memory p ) public { bytes memory signature = vm.randomBytes(32); @@ -41,7 +41,7 @@ contract SignatureGateway_SpokeNotRegistered_Test is SignatureGatewayBaseTest { } function test_borrowWithSig_revertsWith_SpokeNotRegistered( - ISignatureGateway.Borrow memory p + ISignatureGateway.BorrowAction memory p ) public { bytes memory signature = vm.randomBytes(32); @@ -51,7 +51,7 @@ contract SignatureGateway_SpokeNotRegistered_Test is SignatureGatewayBaseTest { } function test_repayWithSig_revertsWith_SpokeNotRegistered( - ISignatureGateway.Repay memory p + ISignatureGateway.RepayAction memory p ) public { bytes memory signature = vm.randomBytes(32); @@ -61,7 +61,7 @@ contract SignatureGateway_SpokeNotRegistered_Test is SignatureGatewayBaseTest { } function test_setUsingAsCollateralWithSig_revertsWith_SpokeNotRegistered( - ISignatureGateway.SetUsingAsCollateral memory p + ISignatureGateway.SetUsingAsCollateralAction memory p ) public { bytes memory signature = vm.randomBytes(32); @@ -71,7 +71,7 @@ contract SignatureGateway_SpokeNotRegistered_Test is SignatureGatewayBaseTest { } function test_updateUserRiskPremiumWithSig_revertsWith_SpokeNotRegistered( - ISignatureGateway.UpdateUserRiskPremium memory p + ISignatureGateway.UpdateUserRiskPremiumAction memory p ) public { bytes memory signature = vm.randomBytes(32); @@ -83,7 +83,7 @@ contract SignatureGateway_SpokeNotRegistered_Test is SignatureGatewayBaseTest { } function test_updateUserDynamicConfigWithSig_revertsWith_SpokeNotRegistered( - ISignatureGateway.UpdateUserDynamicConfig memory p + ISignatureGateway.UpdateUserDynamicConfigAction memory p ) public { bytes memory signature = vm.randomBytes(32); diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.Unauthorized.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.Unauthorized.t.sol index f9343ac35..6ec19e608 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.Unauthorized.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.Reverts.Unauthorized.t.sol @@ -14,7 +14,11 @@ contract SignatureGateway_Unauthorized_PositionManagerNotActive_Test is Signatur } function test_supplyWithSig_revertsWith_Unauthorized() public { - ISignatureGateway.Supply memory p = _supplyData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.SupplyAction memory p = _supplyAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(ISpoke.Unauthorized.selector); @@ -23,7 +27,11 @@ contract SignatureGateway_Unauthorized_PositionManagerNotActive_Test is Signatur } function test_withdrawWithSig_revertsWith_Unauthorized() public { - ISignatureGateway.Withdraw memory p = _withdrawData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.WithdrawAction memory p = _withdrawAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(ISpoke.Unauthorized.selector); @@ -32,7 +40,11 @@ contract SignatureGateway_Unauthorized_PositionManagerNotActive_Test is Signatur } function test_borrowWithSig_revertsWith_Unauthorized() public { - ISignatureGateway.Borrow memory p = _borrowData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.BorrowAction memory p = _borrowAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(ISpoke.Unauthorized.selector); @@ -41,7 +53,11 @@ contract SignatureGateway_Unauthorized_PositionManagerNotActive_Test is Signatur } function test_repayWithSig_revertsWith_Unauthorized() public { - ISignatureGateway.Repay memory p = _repayData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.RepayAction memory p = _repayAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(ISpoke.Unauthorized.selector); @@ -51,7 +67,11 @@ contract SignatureGateway_Unauthorized_PositionManagerNotActive_Test is Signatur function test_setUsingAsCollateralWithSig_revertsWith_Unauthorized() public { uint256 deadline = _warpBeforeRandomDeadline(); - ISignatureGateway.SetUsingAsCollateral memory p = _setAsCollateralData(spoke1, alice, deadline); + ISignatureGateway.SetUsingAsCollateralAction memory p = _setAsCollateralAction( + spoke1, + alice, + deadline + ); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); vm.expectRevert(ISpoke.Unauthorized.selector); @@ -60,7 +80,7 @@ contract SignatureGateway_Unauthorized_PositionManagerNotActive_Test is Signatur } function test_updateUserRiskPremiumWithSig_revertsWith_Unauthorized() public { - ISignatureGateway.UpdateUserRiskPremium memory p = _updateRiskPremiumData( + ISignatureGateway.UpdateUserRiskPremiumAction memory p = _updateRiskPremiumAction( spoke1, alice, _warpBeforeRandomDeadline() @@ -75,7 +95,7 @@ contract SignatureGateway_Unauthorized_PositionManagerNotActive_Test is Signatur } function test_updateUserDynamicConfigWithSig_revertsWith_Unauthorized() public { - ISignatureGateway.UpdateUserDynamicConfig memory p = _updateDynamicConfigData( + ISignatureGateway.UpdateUserDynamicConfigAction memory p = _updateDynamicConfigAction( spoke1, alice, _warpBeforeRandomDeadline() diff --git a/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol b/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol index 2d06d0216..53f0c37b7 100644 --- a/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol +++ b/tests/unit/misc/SignatureGateway/SignatureGateway.t.sol @@ -50,25 +50,29 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { } function test_supplyWithSig() public { - ISignatureGateway.Supply memory p = _supplyData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.SupplyAction memory p = _supplyAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); p.nonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - Utils.approve(spoke1, p.reserveId, alice, address(gateway), p.amount); + Utils.approve(spoke1, p.params.reserveId, alice, address(gateway), p.params.amount); - uint256 shares = _hub(spoke1, p.reserveId).previewAddByAssets( - _spokeAssetId(spoke1, p.reserveId), - p.amount + uint256 shares = _hub(spoke1, p.params.reserveId).previewAddByAssets( + _spokeAssetId(spoke1, p.params.reserveId), + p.params.amount ); TestReturnValues memory returnValues; vm.expectEmit(address(spoke1)); - emit ISpokeBase.Supply(p.reserveId, address(gateway), alice, shares, p.amount); + emit ISpokeBase.Supply(p.params.reserveId, address(gateway), alice, shares, p.params.amount); vm.prank(vm.randomAddress()); (returnValues.shares, returnValues.amount) = gateway.supplyWithSig(p, signature); assertEq(returnValues.shares, shares); - assertEq(returnValues.amount, p.amount); + assertEq(returnValues.amount, p.params.amount); _assertNonceIncrement(gateway, alice, p.nonce); _assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice); @@ -76,25 +80,29 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { } function test_withdrawWithSig() public { - ISignatureGateway.Withdraw memory p = _withdrawData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.WithdrawAction memory p = _withdrawAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); p.nonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - Utils.supply(spoke1, p.reserveId, alice, p.amount + 1, alice); + Utils.supply(spoke1, p.params.reserveId, alice, p.params.amount + 1, alice); - uint256 shares = _hub(spoke1, p.reserveId).previewRemoveByAssets( - _spokeAssetId(spoke1, p.reserveId), - p.amount + uint256 shares = _hub(spoke1, p.params.reserveId).previewRemoveByAssets( + _spokeAssetId(spoke1, p.params.reserveId), + p.params.amount ); TestReturnValues memory returnValues; vm.expectEmit(address(spoke1)); - emit ISpokeBase.Withdraw(p.reserveId, address(gateway), alice, shares, p.amount); + emit ISpokeBase.Withdraw(p.params.reserveId, address(gateway), alice, shares, p.params.amount); vm.prank(vm.randomAddress()); (returnValues.shares, returnValues.amount) = gateway.withdrawWithSig(p, signature); assertEq(returnValues.shares, shares); - assertEq(returnValues.amount, p.amount); + assertEq(returnValues.amount, p.params.amount); _assertNonceIncrement(gateway, alice, p.nonce); _assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice); @@ -102,26 +110,30 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { } function test_borrowWithSig() public { - ISignatureGateway.Borrow memory p = _borrowData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.BorrowAction memory p = _borrowAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); p.nonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf); - p.reserveId = _daiReserveId(spoke1); - p.amount = 1e18; - Utils.supplyCollateral(spoke1, p.reserveId, alice, p.amount * 2, alice); + p.params.reserveId = _daiReserveId(spoke1); + p.params.amount = 1e18; + Utils.supplyCollateral(spoke1, p.params.reserveId, alice, p.params.amount * 2, alice); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - uint256 shares = _hub(spoke1, p.reserveId).previewDrawByAssets( - _spokeAssetId(spoke1, p.reserveId), - p.amount + uint256 shares = _hub(spoke1, p.params.reserveId).previewDrawByAssets( + _spokeAssetId(spoke1, p.params.reserveId), + p.params.amount ); TestReturnValues memory returnValues; vm.expectEmit(address(spoke1)); - emit ISpokeBase.Borrow(p.reserveId, address(gateway), alice, shares, p.amount); + emit ISpokeBase.Borrow(p.params.reserveId, address(gateway), alice, shares, p.params.amount); vm.prank(vm.randomAddress()); (returnValues.shares, returnValues.amount) = gateway.borrowWithSig(p, signature); assertEq(returnValues.shares, shares); - assertEq(returnValues.amount, p.amount); + assertEq(returnValues.amount, p.params.amount); _assertNonceIncrement(gateway, alice, p.nonce); _assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice); @@ -129,41 +141,45 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { } function test_repayWithSig() public { - ISignatureGateway.Repay memory p = _repayData(spoke1, alice, _warpBeforeRandomDeadline()); + ISignatureGateway.RepayAction memory p = _repayAction( + spoke1, + alice, + _warpBeforeRandomDeadline() + ); p.nonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf); - p.reserveId = _daiReserveId(spoke1); - p.amount = 1e18; - Utils.supplyCollateral(spoke1, p.reserveId, alice, p.amount * 2, alice); - Utils.borrow(spoke1, p.reserveId, alice, p.amount, alice); - Utils.approve(spoke1, p.reserveId, alice, address(gateway), p.amount); + p.params.reserveId = _daiReserveId(spoke1); + p.params.amount = 1e18; + Utils.supplyCollateral(spoke1, p.params.reserveId, alice, p.params.amount * 2, alice); + Utils.borrow(spoke1, p.params.reserveId, alice, p.params.amount, alice); + Utils.approve(spoke1, p.params.reserveId, alice, address(gateway), p.params.amount); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); (uint256 baseRestored, uint256 premiumRestored) = _calculateExactRestoreAmount( spoke1, - p.reserveId, + p.params.reserveId, alice, - p.amount + p.params.amount ); - uint256 shares = _hub(spoke1, p.reserveId).previewRestoreByAssets( - _spokeAssetId(spoke1, p.reserveId), + uint256 shares = _hub(spoke1, p.params.reserveId).previewRestoreByAssets( + _spokeAssetId(spoke1, p.params.reserveId), baseRestored ); TestReturnValues memory returnValues; vm.expectEmit(address(spoke1)); emit ISpokeBase.Repay( - p.reserveId, + p.params.reserveId, address(gateway), alice, shares, baseRestored + premiumRestored, - _getExpectedPremiumDelta(spoke1, alice, p.reserveId, premiumRestored) + _getExpectedPremiumDelta(spoke1, alice, p.params.reserveId, premiumRestored) ); vm.prank(vm.randomAddress()); (returnValues.shares, returnValues.amount) = gateway.repayWithSig(p, signature); assertEq(returnValues.shares, shares); - assertEq(returnValues.amount, p.amount); + assertEq(returnValues.amount, p.params.amount); _assertNonceIncrement(gateway, alice, p.nonce); _assertGatewayHasNoBalanceOrAllowance(spoke1, gateway, alice); @@ -172,15 +188,24 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { function test_setUsingAsCollateralWithSig() public { uint256 deadline = _warpBeforeRandomDeadline(); - ISignatureGateway.SetUsingAsCollateral memory p = _setAsCollateralData(spoke1, alice, deadline); + ISignatureGateway.SetUsingAsCollateralAction memory p = _setAsCollateralAction( + spoke1, + alice, + deadline + ); p.nonce = _burnRandomNoncesAtKey(gateway, p.onBehalfOf); - p.reserveId = _daiReserveId(spoke1); - Utils.supplyCollateral(spoke1, p.reserveId, alice, 1e18, alice); + p.params.reserveId = _daiReserveId(spoke1); + Utils.supplyCollateral(spoke1, p.params.reserveId, alice, 1e18, alice); bytes memory signature = _sign(alicePk, _getTypedDataHash(gateway, p)); - if (_isUsingAsCollateral(spoke1, p.reserveId, alice) != p.useAsCollateral) { + if (_isUsingAsCollateral(spoke1, p.params.reserveId, alice) != p.params.useAsCollateral) { vm.expectEmit(address(spoke1)); - emit ISpoke.SetUsingAsCollateral(p.reserveId, address(gateway), alice, p.useAsCollateral); + emit ISpoke.SetUsingAsCollateral( + p.params.reserveId, + address(gateway), + alice, + p.params.useAsCollateral + ); } vm.prank(vm.randomAddress()); @@ -193,7 +218,7 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { function test_updateUserRiskPremiumWithSig() public { uint256 deadline = _warpBeforeRandomDeadline(); - ISignatureGateway.UpdateUserRiskPremium memory p = _updateRiskPremiumData( + ISignatureGateway.UpdateUserRiskPremiumAction memory p = _updateRiskPremiumAction( spoke1, alice, deadline @@ -216,7 +241,7 @@ contract SignatureGatewayTest is SignatureGatewayBaseTest { } function test_updateUserDynamicConfigWithSig() public { - ISignatureGateway.UpdateUserDynamicConfig memory p = _updateDynamicConfigData( + ISignatureGateway.UpdateUserDynamicConfigAction memory p = _updateDynamicConfigAction( spoke1, alice, _warpBeforeRandomDeadline()