|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | +pragma solidity >=0.6.2; |
| 3 | + |
| 4 | +import {DCAExecutionState} from '../hooks/dca/DCAStructs.sol'; |
| 5 | +import {IPreExecutionHook} from './IHook.sol'; |
| 6 | + |
| 7 | +/// @title IDCAHook |
| 8 | +/// @notice Interface for the DCA (Dollar-Cost Averaging) hook contract |
| 9 | +/// @dev Extends IPreExecutionHook to enable periodic execution of DCA intents |
| 10 | +interface IDCAHook is IPreExecutionHook { |
| 11 | + /// @notice Thrown when attempting to cancel an already cancelled intent |
| 12 | + /// @param intentId The identifier of the intent that was already cancelled |
| 13 | + error IntentAlreadyCancelled(bytes32 intentId); |
| 14 | + |
| 15 | + /// @notice Thrown when the swapper signature is invalid |
| 16 | + /// @param recoveredSigner The address recovered from the signature |
| 17 | + /// @param expectedSwapper The expected swapper address |
| 18 | + error InvalidSwapperSignature(address recoveredSigner, address expectedSwapper); |
| 19 | + |
| 20 | + /// @notice Thrown when the cosigner signature is invalid |
| 21 | + /// @param recoveredCosigner The address recovered from the signature |
| 22 | + /// @param expectedCosigner The expected cosigner address |
| 23 | + error InvalidCosignerSignature(address recoveredCosigner, address expectedCosigner); |
| 24 | + |
| 25 | + /// @notice Thrown when the cosigner data swapper doesn't match the intent swapper |
| 26 | + /// @param cosignerSwapper The swapper address in cosigner data |
| 27 | + /// @param intentSwapper The swapper address in the intent |
| 28 | + error CosignerSwapperMismatch(address cosignerSwapper, address intentSwapper); |
| 29 | + |
| 30 | + /// @notice Thrown when the cosigner data nonce doesn't match the intent nonce |
| 31 | + /// @param cosignerNonce The nonce in cosigner data |
| 32 | + /// @param intentNonce The nonce in the intent |
| 33 | + error CosignerNonceMismatch(uint96 cosignerNonce, uint256 intentNonce); |
| 34 | + |
| 35 | + /// @notice Thrown when output allocations array is empty |
| 36 | + error EmptyAllocations(); |
| 37 | + |
| 38 | + /// @notice Thrown when an output allocation has zero basis points |
| 39 | + error ZeroAllocation(); |
| 40 | + |
| 41 | + /// @notice Thrown when allocations don't sum to exactly 100% (10000 basis points) |
| 42 | + /// @param totalBasisPoints The actual sum of basis points |
| 43 | + error AllocationsNot100Percent(uint256 totalBasisPoints); |
| 44 | + |
| 45 | + /// @notice Thrown when multiple allocations have the same recipient |
| 46 | + /// @param recipient The duplicate recipient address |
| 47 | + error DuplicateRecipient(address recipient); |
| 48 | + |
| 49 | + /// @notice Thrown when the hook address doesn't match the expected hook |
| 50 | + /// @param providedHook The hook address provided in the intent |
| 51 | + /// @param expectedHook The expected hook address (this contract) |
| 52 | + error WrongHook(address providedHook, address expectedHook); |
| 53 | + |
| 54 | + /// @notice Thrown when the chain ID doesn't match the current chain |
| 55 | + /// @param providedChainId The chain ID provided in the intent |
| 56 | + /// @param currentChainId The current blockchain's chain ID |
| 57 | + error WrongChain(uint256 providedChainId, uint256 currentChainId); |
| 58 | + |
| 59 | + /// @notice Thrown when the swapper address doesn't match between intent and order |
| 60 | + /// @param orderSwapper The swapper address in the resolved order |
| 61 | + /// @param intentSwapper The swapper address in the intent |
| 62 | + error SwapperMismatch(address orderSwapper, address intentSwapper); |
| 63 | + |
| 64 | + /// @notice Thrown when the input token doesn't match the intent |
| 65 | + /// @param orderInputToken The input token in the resolved order |
| 66 | + /// @param intentInputToken The input token in the intent |
| 67 | + error WrongInputToken(address orderInputToken, address intentInputToken); |
| 68 | + |
| 69 | + /// @notice Thrown when an output token doesn't match the intent |
| 70 | + /// @param outputToken The output token in the resolved order |
| 71 | + /// @param expectedToken The expected output token from the intent |
| 72 | + error WrongOutputToken(address outputToken, address expectedToken); |
| 73 | + |
| 74 | + /// @notice Thrown when chunk size is below minimum allowed |
| 75 | + /// @param amount The actual chunk size (input for EXACT_IN, output for EXACT_OUT) |
| 76 | + /// @param minChunkSize The minimum allowed chunk size |
| 77 | + error ChunkSizeBelowMin(uint256 amount, uint256 minChunkSize); |
| 78 | + |
| 79 | + /// @notice Thrown when chunk size exceeds maximum allowed |
| 80 | + /// @param amount The actual chunk size (input for EXACT_IN, output for EXACT_OUT) |
| 81 | + /// @param maxChunkSize The maximum allowed chunk size |
| 82 | + error ChunkSizeAboveMax(uint256 amount, uint256 maxChunkSize); |
| 83 | + |
| 84 | + /// @notice Thrown when input amount doesn't match execAmount (EXACT_IN) |
| 85 | + /// @param inputAmount The input amount in the order |
| 86 | + /// @param execAmount The expected exec amount from cosigner data |
| 87 | + error InputAmountMismatch(uint256 inputAmount, uint256 execAmount); |
| 88 | + |
| 89 | + /// @notice Thrown when input amount is zero (EXACT_OUT) |
| 90 | + error ZeroInput(); |
| 91 | + |
| 92 | + /// @notice Thrown when input exceeds cosigner's limit (EXACT_OUT) |
| 93 | + /// @param inputAmount The actual input amount |
| 94 | + /// @param limitAmount The cosigner's limit amount |
| 95 | + error InputAboveLimit(uint256 inputAmount, uint256 limitAmount); |
| 96 | + |
| 97 | + /// @notice Thrown when attempting to execute a cancelled intent |
| 98 | + /// @param intentId The identifier of the cancelled intent |
| 99 | + error IntentIsCancelled(bytes32 intentId); |
| 100 | + |
| 101 | + /// @notice Thrown when the intent has expired |
| 102 | + /// @param currentTime The current block timestamp |
| 103 | + /// @param deadline The intent's deadline |
| 104 | + error IntentExpired(uint256 currentTime, uint256 deadline); |
| 105 | + |
| 106 | + /// @notice Thrown when the order nonce doesn't match the expected nonce |
| 107 | + /// @param providedNonce The nonce provided in the cosigner data |
| 108 | + /// @param expectedNonce The expected next nonce for the intent |
| 109 | + error WrongChunkNonce(uint96 providedNonce, uint96 expectedNonce); |
| 110 | + |
| 111 | + /// @notice Thrown when execution is attempted too soon after the last execution |
| 112 | + /// @param elapsed The time elapsed since last execution |
| 113 | + /// @param minPeriod The minimum required period between executions |
| 114 | + error TooSoon(uint256 elapsed, uint256 minPeriod); |
| 115 | + |
| 116 | + /// @notice Thrown when execution is attempted too late after the last execution |
| 117 | + /// @param elapsed The time elapsed since last execution |
| 118 | + /// @param maxPeriod The maximum allowed period between executions |
| 119 | + error TooLate(uint256 elapsed, uint256 maxPeriod); |
| 120 | + |
| 121 | + /// @notice Thrown when the execution price is below the minimum price floor |
| 122 | + /// @param executionPrice The actual execution price (scaled by 1e18) |
| 123 | + /// @param minPrice The minimum acceptable price (scaled by 1e18) |
| 124 | + error PriceBelowMin(uint256 executionPrice, uint256 minPrice); |
| 125 | + |
| 126 | + /// @notice Thrown when output allocation doesn't match expected amount |
| 127 | + /// @param recipient The recipient address |
| 128 | + /// @param actual The actual amount allocated to the recipient |
| 129 | + /// @param expected The expected amount for the recipient |
| 130 | + error AllocationMismatch(address recipient, uint256 actual, uint256 expected); |
| 131 | + |
| 132 | + /// @notice Thrown when total output is insufficient (EXACT_IN) |
| 133 | + /// @param totalOutput The total output amount produced |
| 134 | + /// @param limitAmount The minimum required output amount |
| 135 | + error InsufficientOutput(uint256 totalOutput, uint256 limitAmount); |
| 136 | + |
| 137 | + /// @notice Thrown when total output doesn't match expected amount (EXACT_OUT) |
| 138 | + /// @param totalOutput The actual total output amount |
| 139 | + /// @param execAmount The expected exact output amount |
| 140 | + error WrongTotalOutput(uint256 totalOutput, uint256 execAmount); |
| 141 | + |
| 142 | + /// @notice Emitted when an intent is cancelled |
| 143 | + /// @param intentId The unique identifier of the intent |
| 144 | + /// @param swapper The address of the swapper who cancelled the intent |
| 145 | + event IntentCancelled(bytes32 indexed intentId, address indexed swapper); |
| 146 | + |
| 147 | + /// @notice Emitted when a DCA chunk is executing |
| 148 | + /// @param intentId The unique identifier of the intent |
| 149 | + /// @param execAmount The amount being executed (input for EXACT_IN, output for EXACT_OUT) |
| 150 | + /// @param limitAmount The limit amount (min output for EXACT_IN, max input for EXACT_OUT) |
| 151 | + /// @param totalInputExecuted Cumulative input amount after this execution |
| 152 | + /// @param totalOutput Cumulative output amount after this execution |
| 153 | + event ChunkExecuted( |
| 154 | + bytes32 indexed intentId, |
| 155 | + uint256 execAmount, |
| 156 | + uint256 limitAmount, |
| 157 | + uint256 totalInputExecuted, |
| 158 | + uint256 totalOutput |
| 159 | + ); |
| 160 | + |
| 161 | + /// @notice Cancel a single DCA intent |
| 162 | + /// @param nonce The nonce of the intent to cancel |
| 163 | + /// @dev Only callable by the intent owner (verified via msg.sender and nonce) |
| 164 | + function cancelIntent(uint256 nonce) external; |
| 165 | + |
| 166 | + /// @notice Cancel multiple DCA intents in a single transaction |
| 167 | + /// @param nonces Array of intent nonces to cancel |
| 168 | + /// @dev Only callable by the intent owner for each intent (verified via msg.sender and nonces) |
| 169 | + function cancelIntents(uint256[] calldata nonces) external; |
| 170 | + |
| 171 | + /// @notice Compute the unique identifier for an intent |
| 172 | + /// @param swapper The address of the swapper |
| 173 | + /// @param nonce The nonce of the intent |
| 174 | + /// @return intentId The computed intent identifier |
| 175 | + function computeIntentId(address swapper, uint256 nonce) external pure returns (bytes32); |
| 176 | + |
| 177 | + /// @notice Get the execution state for a specific intent |
| 178 | + /// @param intentId The unique identifier of the intent |
| 179 | + /// @return state The execution state of the intent |
| 180 | + function getExecutionState(bytes32 intentId) external view returns (DCAExecutionState memory state); |
| 181 | + |
| 182 | + /// @notice Check if an intent is currently active (not cancelled and within period/deadline) |
| 183 | + /// @dev Semantics: |
| 184 | + /// - Uninitialized intents (no executed chunks) are considered active unless cancelled or past deadline. |
| 185 | + /// - maxPeriod is enforced only after the first execution; before that, it is ignored. |
| 186 | + /// - A maxPeriod of 0 means no upper bound; a deadline of 0 means no deadline. |
| 187 | + /// @param intentId The unique identifier of the intent |
| 188 | + /// @param maxPeriod The maximum allowed seconds since last execution (0 = no upper bound) |
| 189 | + /// @param deadline The intent expiration timestamp (0 = no deadline) |
| 190 | + /// @return active True if the intent is active, false otherwise |
| 191 | + function isIntentActive(bytes32 intentId, uint256 maxPeriod, uint256 deadline) external view returns (bool active); |
| 192 | + |
| 193 | + /// @notice Get the next expected nonce for an intent |
| 194 | + /// @param intentId The unique identifier of the intent |
| 195 | + /// @return nextNonce The next nonce that should be used for this intent |
| 196 | + function getNextNonce(bytes32 intentId) external view returns (uint96 nextNonce); |
| 197 | + |
| 198 | + /// @notice Get comprehensive statistics for an intent |
| 199 | + /// @param intentId The unique identifier of the intent |
| 200 | + /// @return totalChunks Number of chunks executed |
| 201 | + /// @return totalInput Total input amount executed |
| 202 | + /// @return totalOutput Total output amount received |
| 203 | + /// @return lastExecutionTime Timestamp of last execution |
| 204 | + function getIntentStatistics(bytes32 intentId) |
| 205 | + external |
| 206 | + view |
| 207 | + returns (uint256 totalChunks, uint256 totalInput, uint256 totalOutput, uint256 lastExecutionTime); |
| 208 | +} |
0 commit comments