@@ -8,17 +8,14 @@ import { DonationBox } from "../../chain-adapters/DonationBox.sol";
88// Import MulticallHandler
99import { MulticallHandler } from "../../handlers/MulticallHandler.sol " ;
1010
11- // Import constants
12- import { BPS_SCALAR } from "./Constants.sol " ;
13-
1411/**
15- * @title ArbitraryActionFlowExecutor
12+ * @title ArbitraryEVMFlowExecutor
1613 * @notice Base contract for executing arbitrary action sequences using MulticallHandler
1714 * @dev This contract provides shared functionality for both OFT and CCTP handlers to execute
18- * arbitrary actions on HyperEVM via MulticallHandler, with optional transfer to HyperCore.
15+ * arbitrary actions on HyperEVM via MulticallHandler, returning information about the resulting token amount
1916 * @custom:security-contact bugs@across.to
2017 */
21- abstract contract ArbitraryActionFlowExecutor {
18+ abstract contract ArbitraryEVMFlowExecutor {
2219 using SafeERC20 for IERC20 ;
2320
2421 /// @notice Compressed call struct (no value field to save gas)
@@ -36,9 +33,9 @@ abstract contract ArbitraryActionFlowExecutor {
3633 /// @notice Error thrown when final balance is insufficient
3734 error InsufficientFinalBalance (address token , uint256 expected , uint256 actual );
3835
39- uint256 constant BPS_TOTAL_PRECISION = 18 ;
40- uint256 constant BPS_DECIMALS = 4 ;
41- uint256 constant BPS_PRECISION_SCALAR = 10 ** BPS_TOTAL_PRECISION;
36+ uint256 private constant BPS_TOTAL_PRECISION = 18 ;
37+ uint256 private constant BPS_DECIMALS = 4 ;
38+ uint256 private constant BPS_PRECISION_SCALAR = 10 ** BPS_TOTAL_PRECISION;
4239
4340 constructor (address _multicallHandler ) {
4441 multicallHandler = _multicallHandler;
@@ -49,47 +46,29 @@ abstract contract ArbitraryActionFlowExecutor {
4946 * @dev Decompresses CompressedCall[] to MulticallHandler.Call[] format (adds value: 0)
5047 * @param amount Amount of tokens to transfer to MulticallHandler
5148 * @param quoteNonce Unique nonce for this quote
52- * @param maxBpsToSponsor Maximum basis points to sponsor
5349 * @param initialToken Token to transfer to MulticallHandler
54- * @param finalRecipient Final recipient address
5550 * @param finalToken Expected final token after actions
5651 * @param actionData Encoded actions: abi.encode(CompressedCall[] calls)
57- * @param transferToCore Whether to transfer result to HyperCore
58- * @param extraFeesToSponsor Extra fees to sponsor
52+ * @param extraFeesToSponsorTokenIn Extra fees to sponsor in initialToken
5953 */
60- function _executeArbitraryActionFlow (
54+ function _executeFlow (
6155 uint256 amount ,
6256 bytes32 quoteNonce ,
63- uint256 maxBpsToSponsor ,
6457 address initialToken ,
65- address finalRecipient ,
6658 address finalToken ,
6759 bytes memory actionData ,
68- bool transferToCore ,
69- uint256 extraFeesToSponsor
70- ) internal {
60+ uint256 extraFeesToSponsorTokenIn
61+ ) internal returns (address /* finalToken */ , uint256 finalAmount , uint256 extraFeesToSponsorFinalToken ) {
7162 // Decode the compressed action data
7263 CompressedCall[] memory compressedCalls = abi.decode (actionData, (CompressedCall[]));
7364
74- // Total amount to sponsor is the extra fees to sponsor, ceiling division.
75- uint256 totalAmount = amount + extraFeesToSponsor;
76- uint256 bpsToSponsor = ((extraFeesToSponsor * BPS_PRECISION_SCALAR) + totalAmount - 1 ) / totalAmount;
77- uint256 maxBpsToSponsorAdjusted = maxBpsToSponsor * (10 ** (BPS_TOTAL_PRECISION - BPS_DECIMALS));
78- if (bpsToSponsor > maxBpsToSponsorAdjusted) {
79- bpsToSponsor = maxBpsToSponsorAdjusted;
80- }
81-
8265 // Snapshot balances
8366 uint256 initialAmountSnapshot = IERC20 (initialToken).balanceOf (address (this ));
8467 uint256 finalAmountSnapshot = IERC20 (finalToken).balanceOf (address (this ));
8568
8669 // Transfer tokens to MulticallHandler
8770 IERC20 (initialToken).safeTransfer (multicallHandler, amount);
8871
89- // Decompress calls: add value: 0 to each call and wrap in Instructions
90- // We encode Instructions with calls and a drainLeftoverTokens call at the end
91- uint256 callCount = compressedCalls.length ;
92-
9372 // Build instructions for MulticallHandler
9473 bytes memory instructions = _buildMulticallInstructions (
9574 compressedCalls,
@@ -105,8 +84,6 @@ abstract contract ArbitraryActionFlowExecutor {
10584 instructions
10685 );
10786
108- uint256 finalAmount;
109-
11087 // This means the swap (if one was intended) didn't happen (action failed), so we use the initial token as the final token.
11188 if (initialAmountSnapshot == IERC20 (initialToken).balanceOf (address (this ))) {
11289 finalToken = initialToken;
@@ -122,41 +99,11 @@ abstract contract ArbitraryActionFlowExecutor {
12299 }
123100 }
124101
125- // Apply the bps to sponsor to the final amount to get the amount to sponsor, ceiling division.
126- uint256 bpsToSponsorAdjusted = BPS_PRECISION_SCALAR - bpsToSponsor;
127- uint256 amountToSponsor = (((finalAmount * BPS_PRECISION_SCALAR) + bpsToSponsorAdjusted - 1 ) /
128- bpsToSponsorAdjusted) - finalAmount;
129- if (amountToSponsor > 0 ) {
130- DonationBox donationBox = _getDonationBox ();
131- if (IERC20 (finalToken).balanceOf (address (donationBox)) < amountToSponsor) {
132- amountToSponsor = 0 ;
133- } else {
134- donationBox.withdraw (IERC20 (finalToken), amountToSponsor);
135- }
136- }
102+ extraFeesToSponsorFinalToken = _calcExtraFeesFinal (amount, extraFeesToSponsorTokenIn, finalAmount);
137103
138- emit ArbitraryActionsExecuted (quoteNonce, callCount, finalAmount);
139-
140- // Route to appropriate destination based on transferToCore flag
141- if (transferToCore) {
142- _executeSimpleTransferFlow (
143- finalAmount,
144- quoteNonce,
145- maxBpsToSponsor,
146- finalRecipient,
147- amountToSponsor,
148- finalToken
149- );
150- } else {
151- _fallbackHyperEVMFlow (
152- finalAmount,
153- quoteNonce,
154- maxBpsToSponsor,
155- finalRecipient,
156- amountToSponsor,
157- finalToken
158- );
159- }
104+ emit ArbitraryActionsExecuted (quoteNonce, compressedCalls.length , finalAmount);
105+
106+ return (finalToken, finalAmount, extraFeesToSponsorFinalToken);
160107 }
161108
162109 /**
@@ -202,37 +149,25 @@ abstract contract ArbitraryActionFlowExecutor {
202149 return abi.encode (instructions);
203150 }
204151
205- /**
206- * @notice Execute simple transfer flow to HyperCore with the final token
207- * @dev Must be implemented by contracts that inherit from this contract
208- */
209- function _executeSimpleTransferFlow (
210- uint256 finalAmount ,
211- bytes32 quoteNonce ,
212- uint256 maxBpsToSponsor ,
213- address finalRecipient ,
214- uint256 extraFeesToSponsor ,
215- address finalToken
216- ) internal virtual ;
217-
218- /**
219- * @notice Execute fallback HyperEVM flow (stay on HyperEVM)
220- * @dev Must be implemented by contracts that inherit from this contract
221- */
222- function _fallbackHyperEVMFlow (
223- uint256 finalAmount ,
224- bytes32 quoteNonce ,
225- uint256 maxBpsToSponsor ,
226- address finalRecipient ,
227- uint256 extraFeesToSponsor ,
228- address finalToken
229- ) internal virtual ;
152+ /// @notice Calcualtes proportional fees to sponsor in finalToken, given the fees to sponsor in initial token and initial amount
153+ function _calcExtraFeesFinal (
154+ uint256 amount ,
155+ uint256 extraFeesToSponsorTokenIn ,
156+ uint256 finalAmount
157+ ) internal pure returns (uint256 extraFeesToSponsorFinalToken ) {
158+ // Total amount to sponsor is the extra fees to sponsor, ceiling division.
159+ uint256 bpsToSponsor;
160+ {
161+ uint256 totalAmount = amount + extraFeesToSponsorTokenIn;
162+ bpsToSponsor = ((extraFeesToSponsorTokenIn * BPS_PRECISION_SCALAR) + totalAmount - 1 ) / totalAmount;
163+ }
230164
231- /**
232- * @notice Get the donation box instance
233- * @dev Must be implemented by contracts that inherit from this contract
234- */
235- function _getDonationBox () internal view virtual returns (DonationBox);
165+ // Apply the bps to sponsor to the final amount to get the amount to sponsor, ceiling division.
166+ uint256 bpsToSponsorAdjusted = BPS_PRECISION_SCALAR - bpsToSponsor;
167+ extraFeesToSponsorFinalToken =
168+ (((finalAmount * BPS_PRECISION_SCALAR) + bpsToSponsorAdjusted - 1 ) / bpsToSponsorAdjusted) -
169+ finalAmount;
170+ }
236171
237172 /// @notice Allow contract to receive native tokens for arbitrary action execution
238173 receive () external payable virtual {}
0 commit comments