@@ -68,6 +68,14 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
6868 uint256 internal constant _ATTEST_EMPTY_BLOCK_TYPE_HASH =
6969 0x3f25e551746414ff93f076a7dd83828ff53735b39366c74015637e004fcb0223 ;
7070
71+ /// @dev `keccak256("SwapExactIn(SwapPathExactAmountIn[] paths, uint64 block_number)")`.
72+ uint256 internal constant _SWAP_EXACT_IN_TYPE_HASH =
73+ 0xc3810e534961c3152a90c6e5342d0ef3cdd6517c38c42e01b7640beffc3e41c2 ;
74+
75+ /// @dev `keccak256("SwapExactOut(SwapPathExactAmountOut[] paths, uint64 block_number)")`.
76+ uint256 internal constant _SWAP_EXACT_OUT_TYPE_HASH =
77+ 0xb26cc9223a5f7a414a15401ce11a9ef78c5c9daf4bc40c11e20d3f53cb9d79a5 ;
78+
7179 /// @dev Set of active Angstrom validator nodes, authorized to unlock this contract for operations.
7280 mapping (address node = > bool isActive ) internal _angstromValidatorNodes;
7381
@@ -159,7 +167,7 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
159167 abi.decode (
160168 _vault.unlock (
161169 abi.encodeCall (
162- BatchRouterHooks.swapExactInHook ,
170+ AngstromBalancer.swapExactInHookAngstrom ,
163171 SwapExactInHookParams ({
164172 sender: msg .sender ,
165173 paths: paths,
@@ -173,6 +181,32 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
173181 );
174182 }
175183
184+ function swapExactInHookAngstrom (
185+ SwapExactInHookParams calldata params
186+ )
187+ external
188+ nonReentrant
189+ onlyVault
190+ returns (uint256 [] memory pathAmountsOut , address [] memory tokensOut , uint256 [] memory amountsOut )
191+ {
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.
199+ bytes32 digest = _computeDigestSwapExactIn (params.paths);
200+
201+ if (SignatureCheckerLib.isValidSignatureNow (payer, digest, signature) == false ) {
202+ revert InvalidSignature ();
203+ }
204+
205+ (pathAmountsOut, tokensOut, amountsOut) = _swapExactInHook (params);
206+
207+ _settlePaths (payer, params.wethIsEth);
208+ }
209+
176210 /// @inheritdoc IBatchRouter
177211 function swapExactOut (
178212 SwapPathExactAmountOut[] memory paths ,
@@ -193,7 +227,7 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
193227 abi.decode (
194228 _vault.unlock (
195229 abi.encodeCall (
196- BatchRouterHooks.swapExactOutHook ,
230+ AngstromBalancer.swapExactOutHookAngstrom ,
197231 SwapExactOutHookParams ({
198232 sender: msg .sender ,
199233 paths: paths,
@@ -207,6 +241,32 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
207241 );
208242 }
209243
244+ function swapExactOutHookAngstrom (
245+ SwapExactOutHookParams calldata params
246+ )
247+ external
248+ nonReentrant
249+ onlyVault
250+ returns (uint256 [] memory pathAmountsIn , address [] memory tokensIn , uint256 [] memory amountsIn )
251+ {
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.
259+ bytes32 digest = _computeDigestSwapExactOut (params.paths);
260+
261+ if (SignatureCheckerLib.isValidSignatureNow (payer, digest, signature) == false ) {
262+ revert InvalidSignature ();
263+ }
264+
265+ (pathAmountsIn, tokensIn, amountsIn) = _swapExactOutHook (params);
266+
267+ _settlePaths (payer, params.wethIsEth);
268+ }
269+
210270 /***************************************************************************
211271 Queries
212272 ***************************************************************************/
@@ -447,6 +507,88 @@ contract AngstromBalancer is IBatchRouter, BatchRouterHooks, OwnableAuthenticati
447507 }
448508 }
449509
510+ function _computeDigestSwapExactIn (SwapPathExactAmountIn[] memory paths ) internal view returns (bytes32 ) {
511+ // First, hash the paths array according to EIP-712
512+ bytes32 pathsHash = _hashSwapExactInPathArray (paths);
513+
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);
524+ }
525+
526+ // Helper function to hash the SwapPathExactAmountIn array
527+ function _hashSwapExactInPathArray (SwapPathExactAmountIn[] memory paths ) internal pure returns (bytes32 ) {
528+ bytes32 [] memory pathHashes = new bytes32 [](paths.length );
529+
530+ for (uint256 i = 0 ; i < paths.length ; i++ ) {
531+ pathHashes[i] = _hashSwapExactInPath (paths[i]);
532+ }
533+
534+ return keccak256 (abi.encodePacked (pathHashes));
535+ }
536+
537+ // Helper function to hash a single SwapPathExactAmountIn
538+ 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)
541+ bytes32 [] memory stepHashes = new bytes32 [](path.steps.length );
542+
543+ for (uint256 i = 0 ; i < path.steps.length ; i++ ) {
544+ stepHashes[i] = keccak256 (abi.encode (path.steps[i].pool, path.steps[i].tokenOut, path.steps[i].isBuffer));
545+ }
546+
547+ bytes32 stepsHash = keccak256 (abi.encodePacked (stepHashes));
548+
549+ return keccak256 (abi.encode (path.tokenIn, stepsHash, path.exactAmountIn, path.minAmountOut));
550+ }
551+
552+ function _computeDigestSwapExactOut (SwapPathExactAmountOut[] memory paths ) internal view returns (bytes32 ) {
553+ // First, hash the paths array according to EIP-712
554+ bytes32 pathsHash = _hashSwapExactOutPathArray (paths);
555+
556+ bytes32 swapExactOutStructHash;
557+ // solhint-disable-next-line no-inline-assembly
558+ assembly ("memory-safe" ) {
559+ let ptr := mload (0x40 )
560+ mstore (ptr, _SWAP_EXACT_OUT_TYPE_HASH)
561+ mstore (add (ptr, 0x20 ), pathsHash)
562+ mstore (add (ptr, 0x40 ), number ())
563+ swapExactOutStructHash := keccak256 (ptr, 0x60 )
564+ }
565+ return _hashTypedData (swapExactOutStructHash);
566+ }
567+
568+ // Helper function to hash the SwapPathExactAmountOut array
569+ function _hashSwapExactOutPathArray (SwapPathExactAmountOut[] memory paths ) internal pure returns (bytes32 ) {
570+ bytes32 [] memory pathHashes = new bytes32 [](paths.length );
571+
572+ for (uint256 i = 0 ; i < paths.length ; i++ ) {
573+ pathHashes[i] = _hashSwapExactOutPath (paths[i]);
574+ }
575+
576+ return keccak256 (abi.encodePacked (pathHashes));
577+ }
578+
579+ // Helper function to hash a single SwapPathExactAmountOut
580+ function _hashSwapExactOutPath (SwapPathExactAmountOut memory path ) internal pure returns (bytes32 ) {
581+ bytes32 [] memory stepHashes = new bytes32 [](path.steps.length );
582+
583+ for (uint256 i = 0 ; i < path.steps.length ; i++ ) {
584+ stepHashes[i] = keccak256 (abi.encode (path.steps[i].pool, path.steps[i].tokenOut, path.steps[i].isBuffer));
585+ }
586+
587+ bytes32 stepsHash = keccak256 (abi.encodePacked (stepHashes));
588+
589+ return keccak256 (abi.encode (path.tokenIn, stepsHash, path.maxAmountIn, path.exactAmountOut));
590+ }
591+
450592 function _getDigest () internal view returns (bytes32 ) {
451593 bytes32 attestationStructHash;
452594 // solhint-disable-next-line no-inline-assembly
0 commit comments