Skip to content

Commit 54c5271

Browse files
committed
Implement swapper different from node.
1 parent ff96022 commit 54c5271

File tree

5 files changed

+302
-13
lines changed

5 files changed

+302
-13
lines changed

contracts/AngstromBalancer.sol

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

contracts/test/AngstromBalancerMock.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { IPermit2 } from "permit2/src/interfaces/IPermit2.sol";
66

77
import { IWETH } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/misc/IWETH.sol";
88
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";
9+
import "@balancer-labs/v3-interfaces/contracts/vault/BatchRouterTypes.sol";
910

1011
import { AngstromBalancer } from "../AngstromBalancer.sol";
1112

@@ -23,6 +24,14 @@ contract AngstromBalancerMock is AngstromBalancer {
2324
_unlockAngstrom();
2425
}
2526

27+
function computeDigestSwapExactIn(SwapPathExactAmountIn[] memory paths) external view returns (bytes32) {
28+
return _computeDigestSwapExactIn(paths);
29+
}
30+
31+
function computeDigestSwapExactOut(SwapPathExactAmountOut[] memory paths) external view returns (bytes32) {
32+
return _computeDigestSwapExactOut(paths);
33+
}
34+
2635
function getDigest() external view returns (bytes32) {
2736
return _getDigest();
2837
}

test/foundry/AngstromHook.t.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ contract AngstromHookTest is BaseAngstromTest {
4242

4343
function testOnBeforeSwapInvalidSignature() public {
4444
// If the signature is invalid, the hook reverts (in this case, signer and key do not match).
45-
(, bytes memory userData) = generateSignatureAndUserData(bob, aliceKey);
45+
(, bytes memory userData) = generateSignatureAndUserDataEmptyAttestation(bob, aliceKey);
4646

4747
registerAngstromNode(bob);
4848

@@ -109,7 +109,7 @@ contract AngstromHookTest is BaseAngstromTest {
109109
}
110110

111111
function testOnBeforeAddLiquidityUnbalancedInvalidSignature() public {
112-
(, bytes memory userData) = generateSignatureAndUserData(alice, bobKey);
112+
(, bytes memory userData) = generateSignatureAndUserDataEmptyAttestation(alice, bobKey);
113113

114114
registerAngstromNode(alice);
115115

@@ -171,7 +171,7 @@ contract AngstromHookTest is BaseAngstromTest {
171171
}
172172

173173
function testOnBeforeRemoveLiquidityUnbalancedInvalidSignature() public {
174-
(, bytes memory userData) = generateSignatureAndUserData(lp, bobKey);
174+
(, bytes memory userData) = generateSignatureAndUserDataEmptyAttestation(lp, bobKey);
175175

176176
registerAngstromNode(lp);
177177

0 commit comments

Comments
 (0)