@@ -76,6 +76,8 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
7676 uint256 internal constant _SWAP_EXACT_OUT_TYPE_HASH =
7777 0xb26cc9223a5f7a414a15401ce11a9ef78c5c9daf4bc40c11e20d3f53cb9d79a5 ;
7878
79+ uint256 internal constant _MINIMUM_USER_DATA_LENGTH = 20 ;
80+
7981 /// @dev Set of active Angstrom validator nodes, authorized to unlock this contract for operations.
8082 mapping (address node = > bool isActive ) internal _angstromValidatorNodes;
8183
@@ -134,6 +136,11 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
134136 _;
135137 }
136138
139+ modifier withValidUserData (bytes calldata userData ) {
140+ _ensureUserData (userData);
141+ _;
142+ }
143+
137144 constructor (
138145 IVault vault ,
139146 IWETH weth ,
@@ -167,7 +174,7 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
167174 abi.decode (
168175 _vault.unlock (
169176 abi.encodeCall (
170- AngstromBalancer.swapExactInHookAngstrom ,
177+ AngstromBalancer.swapExactInAngstromHook ,
171178 SwapExactInHookParams ({
172179 sender: msg .sender ,
173180 paths: paths,
@@ -181,26 +188,19 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
181188 );
182189 }
183190
184- function swapExactInHookAngstrom (
191+ function swapExactInAngstromHook (
185192 SwapExactInHookParams calldata params
186193 )
187194 external
188195 nonReentrant
189196 onlyVault
197+ withValidUserData (params.userData)
190198 returns (uint256 [] memory pathAmountsOut , address [] memory tokensOut , uint256 [] memory amountsOut )
191199 {
192- // validate signature and sender.
193- if (params.userData.length < 20 ) {
194- revert InvalidSignature ();
195- }
196-
197- (address payer , bytes memory signature ) = _splitUserData (params.userData);
198- // The signature looks well-formed.
199200 bytes32 digest = _computeDigestSwapExactIn (params.paths);
200201
201- if (SignatureCheckerLib.isValidSignatureNow (payer, digest, signature) == false ) {
202- revert InvalidSignature ();
203- }
202+ // This reverts if the signature is invalid.
203+ address payer = _extractPayerWithValidSignature (digest, params.userData);
204204
205205 (pathAmountsOut, tokensOut, amountsOut) = _swapExactInHook (params);
206206
@@ -227,7 +227,7 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
227227 abi.decode (
228228 _vault.unlock (
229229 abi.encodeCall (
230- AngstromBalancer.swapExactOutHookAngstrom ,
230+ AngstromBalancer.swapExactOutAngstromHook ,
231231 SwapExactOutHookParams ({
232232 sender: msg .sender ,
233233 paths: paths,
@@ -241,26 +241,19 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
241241 );
242242 }
243243
244- function swapExactOutHookAngstrom (
244+ function swapExactOutAngstromHook (
245245 SwapExactOutHookParams calldata params
246246 )
247247 external
248248 nonReentrant
249249 onlyVault
250+ withValidUserData (params.userData)
250251 returns (uint256 [] memory pathAmountsIn , address [] memory tokensIn , uint256 [] memory amountsIn )
251252 {
252- // validate signature and sender.
253- if (params.userData.length < 20 ) {
254- revert InvalidSignature ();
255- }
256-
257- (address payer , bytes memory signature ) = _splitUserData (params.userData);
258- // The signature looks well-formed.
259253 bytes32 digest = _computeDigestSwapExactOut (params.paths);
260254
261- if (SignatureCheckerLib.isValidSignatureNow (payer, digest, signature) == false ) {
262- revert InvalidSignature ();
263- }
255+ // This reverts if the signature is invalid.
256+ address payer = _extractPayerWithValidSignature (digest, params.userData);
264257
265258 (pathAmountsIn, tokensIn, amountsIn) = _swapExactOutHook (params);
266259
@@ -497,33 +490,25 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
497490 function _unlockAngstromWithSignature (bytes memory userData ) internal {
498491 // Queries are always allowed.
499492 if (_isAngstromUnlocked () == false && EVMCallModeHelpers.isStaticCall () == false ) {
500- if (userData.length < 20 ) {
493+ if (userData.length < _MINIMUM_USER_DATA_LENGTH ) {
501494 revert InvalidSignature ();
502495 } else {
503- (address node , bytes memory signature ) = _splitUserData (userData);
496+ (address node , bytes memory signature ) = _splitUserDataMemory (userData);
504497 // The signature looks well-formed. Revert if it doesn't correspond to a registered node.
505498 _unlockWithEmptyAttestation (node, signature);
506499 }
507500 }
508501 }
509502
510503 function _computeDigestSwapExactIn (SwapPathExactAmountIn[] memory paths ) internal view returns (bytes32 ) {
511- // First, hash the paths array according to EIP-712
504+ // First, hash the paths array according to EIP-712.
512505 bytes32 pathsHash = _hashSwapExactInPathArray (paths);
506+ bytes32 structHash = _computeStructHashWithBlockNumber (_SWAP_EXACT_IN_TYPE_HASH, pathsHash);
513507
514- bytes32 swapExactInStructHash;
515- // solhint-disable-next-line no-inline-assembly
516- assembly ("memory-safe" ) {
517- let ptr := mload (0x40 )
518- mstore (ptr, _SWAP_EXACT_IN_TYPE_HASH)
519- mstore (add (ptr, 0x20 ), pathsHash)
520- mstore (add (ptr, 0x40 ), number ())
521- swapExactInStructHash := keccak256 (ptr, 0x60 )
522- }
523- return _hashTypedData (swapExactInStructHash);
508+ return _hashTypedData (structHash);
524509 }
525510
526- // Helper function to hash the SwapPathExactAmountIn array
511+ // Helper function to hash the SwapPathExactAmountIn array.
527512 function _hashSwapExactInPathArray (SwapPathExactAmountIn[] memory paths ) internal pure returns (bytes32 ) {
528513 bytes32 [] memory pathHashes = new bytes32 [](paths.length );
529514
@@ -536,8 +521,8 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
536521
537522 // Helper function to hash a single SwapPathExactAmountIn
538523 function _hashSwapExactInPath (SwapPathExactAmountIn memory path ) internal pure returns (bytes32 ) {
539- // You'll need to define the type hash for SwapPathExactAmountIn
540- // For now, using a simple encoding (adjust based on your EIP-712 schema)
524+ // We need to define a type hash for SwapPathExactAmountIn.
525+ // For now, using a simple encoding (adjust based on the EIP-712 schema).
541526 bytes32 [] memory stepHashes = new bytes32 [](path.steps.length );
542527
543528 for (uint256 i = 0 ; i < path.steps.length ; i++ ) {
@@ -550,22 +535,28 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
550535 }
551536
552537 function _computeDigestSwapExactOut (SwapPathExactAmountOut[] memory paths ) internal view returns (bytes32 ) {
553- // First, hash the paths array according to EIP-712
538+ // First, hash the paths array according to EIP-712.
554539 bytes32 pathsHash = _hashSwapExactOutPathArray (paths);
540+ bytes32 structHash = _computeStructHashWithBlockNumber (_SWAP_EXACT_OUT_TYPE_HASH, pathsHash);
555541
556- bytes32 swapExactOutStructHash;
542+ return _hashTypedData (structHash);
543+ }
544+
545+ function _computeStructHashWithBlockNumber (uint256 typeHash , bytes32 contentHash ) internal view returns (bytes32 ) {
546+ bytes32 structHash;
557547 // solhint-disable-next-line no-inline-assembly
558548 assembly ("memory-safe" ) {
559549 let ptr := mload (0x40 )
560- mstore (ptr, _SWAP_EXACT_OUT_TYPE_HASH )
561- mstore (add (ptr, 0x20 ), pathsHash )
550+ mstore (ptr, typeHash )
551+ mstore (add (ptr, 0x20 ), contentHash )
562552 mstore (add (ptr, 0x40 ), number ())
563- swapExactOutStructHash := keccak256 (ptr, 0x60 )
553+ structHash := keccak256 (ptr, 0x60 )
564554 }
565- return _hashTypedData (swapExactOutStructHash);
555+
556+ return structHash;
566557 }
567558
568- // Helper function to hash the SwapPathExactAmountOut array
559+ // Helper function to hash the SwapPathExactAmountOut array.
569560 function _hashSwapExactOutPathArray (SwapPathExactAmountOut[] memory paths ) internal pure returns (bytes32 ) {
570561 bytes32 [] memory pathHashes = new bytes32 [](paths.length );
571562
@@ -576,8 +567,10 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
576567 return keccak256 (abi.encodePacked (pathHashes));
577568 }
578569
579- // Helper function to hash a single SwapPathExactAmountOut
570+ // Helper function to hash a single SwapPathExactAmountOut.
580571 function _hashSwapExactOutPath (SwapPathExactAmountOut memory path ) internal pure returns (bytes32 ) {
572+ // We need to define a type hash for SwapPathExactAmountOut.
573+ // For now, using a simple encoding (adjust based on the EIP-712 schema).
581574 bytes32 [] memory stepHashes = new bytes32 [](path.steps.length );
582575
583576 for (uint256 i = 0 ; i < path.steps.length ; i++ ) {
@@ -590,19 +583,16 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
590583 }
591584
592585 function _getDigest () internal view returns (bytes32 ) {
593- bytes32 attestationStructHash;
594- // solhint-disable-next-line no-inline-assembly
595- assembly ("memory-safe" ) {
596- mstore (0x00 , _ATTEST_EMPTY_BLOCK_TYPE_HASH)
597- mstore (0x20 , number ())
598- attestationStructHash := keccak256 (0x00 , 0x40 )
599- }
600- return _hashTypedData (attestationStructHash);
586+ bytes32 structHash = _computeStructHashWithBlockNumber (
587+ _ATTEST_EMPTY_BLOCK_TYPE_HASH,
588+ bytes32 (0 ) // no content for empty attestation
589+ );
590+ return _hashTypedData (structHash);
601591 }
602592
603593 // The first 20 bytes of the user data is the node address; the rest is the signature.
604594 // This function separates the two so that the node signature can be verified.
605- function _splitUserData (
595+ function _splitUserDataMemory (
606596 bytes memory userData
607597 ) internal pure returns (address extractedAddress , bytes memory hashedMessage ) {
608598 uint256 signatureLength = userData.length - 20 ;
@@ -620,6 +610,13 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
620610 }
621611 }
622612
613+ function _splitUserData (
614+ bytes calldata userData
615+ ) internal pure returns (address extractedAddress , bytes calldata signature ) {
616+ extractedAddress = address (bytes20 (userData[0 :20 ]));
617+ signature = userData[20 :];
618+ }
619+
623620 // Signature passed in memory (from userData).
624621 function _unlockWithEmptyAttestation (address node , bytes memory signature ) internal {
625622 bytes32 digest = _ensureRegisteredNodeAndReturnDigest (node);
@@ -650,6 +647,25 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
650647 }
651648 }
652649
650+ function _ensureUserData (bytes calldata userData ) internal pure {
651+ // Basic length validation of the user data, before splitting and signature validation.
652+ if (userData.length < _MINIMUM_USER_DATA_LENGTH) {
653+ revert InvalidSignature ();
654+ }
655+ }
656+
657+ function _extractPayerWithValidSignature (
658+ bytes32 digest ,
659+ bytes calldata userData
660+ ) internal view returns (address payer ) {
661+ bytes memory signature;
662+ (payer, signature) = _splitUserData (userData);
663+
664+ if (SignatureCheckerLib.isValidSignatureNow (payer, digest, signature) == false ) {
665+ revert InvalidSignature ();
666+ }
667+ }
668+
653669 function _unlockAngstrom () internal {
654670 _lastUnlockBlockNumber = block .number ;
655671 }
0 commit comments