Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/contracts/intent/FeeAdapterV2.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {Ownable, Ownable2Step} from '@openzeppelin/contracts/access/Ownable2Step.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import {MessageHashUtils} from '@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol';
import {TypeCasts} from 'contracts/common/TypeCasts.sol';
import {IEverclearV2} from 'interfaces/common/IEverclearV2.sol';
import {IPermit2} from 'interfaces/common/IPermit2.sol';
import {IEverclearSpokeV6} from 'interfaces/intent/IEverclearSpokeV6.sol';
import {IFeeAdapterV2} from 'interfaces/intent/IFeeAdapterV2.sol';
contract FeeAdapterV2 is IFeeAdapterV2, Ownable2Step {
////////////////////
//// Libraries /////
////////////////////
using SafeERC20 for IERC20;
using TypeCasts for address;
using TypeCasts for bytes32;
////////////////////
///// Storage //////
////////////////////
/// @inheritdoc IFeeAdapterV2
IPermit2 public constant PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3);
/// @inheritdoc IFeeAdapterV2
IEverclearSpokeV6 public immutable spoke;
// @inheritdoc IFeeAdapterV2
address public immutable xerc20Module;
/// @inheritdoc IFeeAdapterV2
address public feeRecipient;
/// @inheritdoc IFeeAdapterV2
address public feeSigner;
/// @inheritdoc IFeeAdapterV2
mapping(bytes32 => bool) public txExists;
////////////////////
/// Constructor ////
////////////////////
constructor(
address _spoke,
address _feeRecipient,
address _feeSigner,
address _xerc20Module,
address _owner
) Ownable(_owner) {
spoke = IEverclearSpokeV6(_spoke);
xerc20Module = _xerc20Module;
_updateFeeRecipient(_feeRecipient);
_updateFeeSigner(_feeSigner);
}
////////////////////
////// Admin ///////
////////////////////
/// @inheritdoc IFeeAdapterV2
function updateFeeRecipient(
address _feeRecipient
) external onlyOwner {
_updateFeeRecipient(_feeRecipient);
}
/// @inheritdoc IFeeAdapterV2
function updateFeeSigner(
address _feeSigner
) external onlyOwner {
_updateFeeSigner(_feeSigner);
}
/// @inheritdoc IFeeAdapterV2
function returnUnsupportedIntent(
address _asset,
uint256 _amount,
address _recipient
) external onlyOwner {
spoke.withdraw(_asset, _amount);
_pushTokens(_recipient, _asset, _amount);
}
////////////////////
///// External /////
////////////////////
/// @inheritdoc IFeeAdapterV2
function newIntent(
uint32[] memory _destinations,
bytes32 _receiver,
address _inputAsset,
bytes32 _outputAsset,
uint256 _amount,
uint256 _amountOutMin,
uint48 _ttl,
bytes calldata _data,
IFeeAdapterV2.FeeParams calldata _feeParams
) external payable returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
// Transfer from caller
_pullTokens(msg.sender, _inputAsset, _amount + _feeParams.fee);
// Create intent
(_intentId, _intent) =
_newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data, _feeParams);
}
/// @inheritdoc IFeeAdapterV2
function newIntent(
uint32[] memory _destinations,
address _receiver,
address _inputAsset,
address _outputAsset,
uint256 _amount,
uint256 _amountOutMin,
uint48 _ttl,
bytes calldata _data,
IFeeAdapterV2.FeeParams calldata _feeParams
) external payable returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
// Transfer from caller
_pullTokens(msg.sender, _inputAsset, _amount + _feeParams.fee);
// Create intent
(_intentId, _intent) =
_newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data, _feeParams);
}
/// @inheritdoc IFeeAdapterV2
function newIntent(
uint32[] memory _destinations,
address _receiver,
address _inputAsset,
address _outputAsset,
uint256 _amount,
uint256 _amountOutMin,
uint48 _ttl,
bytes calldata _data,
IEverclearSpokeV6.Permit2Params calldata _permit2Params,
IFeeAdapterV2.FeeParams calldata _feeParams
) external payable returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
// Transfer from caller using permit2
_pullWithPermit2(_inputAsset, _amount + _feeParams.fee, _permit2Params);
// Call internal helper to create intent
(_intentId, _intent) =
_newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data, _feeParams);
}
/// @inheritdoc IFeeAdapterV2
function newOrderSplitEvenly(
uint32 _numIntents,
uint256 _fee,
uint256 _deadline,
bytes calldata _sig,
OrderParameters memory _params
) external payable returns (bytes32 _orderId, bytes32[] memory _intentIds) {
// Transfer once from the user
_pullTokens(msg.sender, _params.inputAsset, _params.amount + _fee);
// Send fees to recipient
bytes32 _sigData = keccak256(abi.encode(msg.value, _numIntents, _params, _fee, _deadline));
_verifySignature(_sigData, _sig);
_handleFees(_fee, msg.value, _params.inputAsset, _deadline);
// Approve the spoke contract if needed
_approveSpokeIfNeeded(_params.inputAsset, _params.amount);
// Create `_numIntents` intents with the same params and `_amount` divided
// equally across all created intents.
uint256 _toSend = _params.amount / _numIntents;
// Initialising array length
_intentIds = new bytes32[](_numIntents);
for (uint256 i; i < _numIntents - 1; i++) {
// Create new intent
(bytes32 _intentId,) = spoke.newIntent(
_params.destinations,
_params.receiver,
_params.inputAsset,
_params.outputAsset,
_toSend,
_params.amountOutMin,
_params.ttl,
_params.data
);
_intentIds[i] = _intentId;
}
// Create a final intent here with the remainder of balance
_toSend = _toSend * (_numIntents - 1);
(bytes32 _intentId,) = spoke.newIntent(
_params.destinations,
_params.receiver,
_params.inputAsset,
_params.outputAsset,
_params.amount - _toSend, // handles remainder gracefully
_params.amountOutMin,
_params.ttl,
_params.data
);
// Add to array
_intentIds[_numIntents - 1] = _intentId;
// Calculate order id
_orderId = keccak256(abi.encode(_intentIds));
// Emit order information event
emit OrderCreated(_orderId, msg.sender.toBytes32(), _intentIds, _fee, msg.value);
}
/// @inheritdoc IFeeAdapterV2
function newOrder(
uint256 _fee,
uint256 _deadline,
bytes calldata _sig,
OrderParameters[] memory _params
) external payable returns (bytes32 _orderId, bytes32[] memory _intentIds) {
uint256 _numIntents = _params.length;
{
// Get the asset
address _inputAsset = _params[0].inputAsset;
// Get the sum of the order amounts
uint256 _orderSum;
for (uint256 i; i < _numIntents; i++) {
_orderSum += _params[i].amount;
if (_params[i].inputAsset != _inputAsset) {
revert MultipleOrderAssets();
}
}
// Transfer once from the user
_pullTokens(msg.sender, _inputAsset, _orderSum + _fee);
// Approve the spoke contract if needed
_approveSpokeIfNeeded(_inputAsset, _orderSum);
// Send fees to recipient
bytes32 _sigData = keccak256(abi.encode(msg.value, _params, _fee, _deadline));
_verifySignature(_sigData, _sig);
_handleFees(_fee, msg.value, _inputAsset, _deadline);
}
// Initialising array length
_intentIds = new bytes32[](_numIntents);
for (uint256 i; i < _numIntents; i++) {
// Create new intent
(bytes32 _intentId,) = spoke.newIntent(
_params[i].destinations,
_params[i].receiver,
_params[i].inputAsset,
_params[i].outputAsset,
_params[i].amount,
_params[i].amountOutMin,
_params[i].ttl,
_params[i].data
);
_intentIds[i] = _intentId;
}
// Calculate order id
_orderId = keccak256(abi.encode(_intentIds));
// Emit order event
emit OrderCreated(_orderId, msg.sender.toBytes32(), _intentIds, _fee, msg.value);
}
////////////////////
///// Internal /////
////////////////////
/**
* @notice Internal function to create a new intent
* @param _destinations Array of destination chain IDs
* @param _receiver Address of the receiver on the destination chain
* @param _inputAsset Address of the input asset
* @param _outputAsset Address of the output asset
* @param _amount Amount of input asset to transfer
* @param _amountOutMin Maximum fee in basis points that can be charged
* @param _ttl Time-to-live for the intent
* @param _data Additional data for the intent
* @param _feeParams Fee parameters including fee amount, deadline, and signature
* @return _intentId The ID of the created intent
* @return _intent The created intent object
*/
function _newIntent(
uint32[] memory _destinations,
bytes32 _receiver,
address _inputAsset,
bytes32 _outputAsset,
uint256 _amount,
uint256 _amountOutMin,
uint48 _ttl,
bytes calldata _data,
IFeeAdapterV2.FeeParams calldata _feeParams
) internal returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
// Send fees to recipient
bytes32 _sigData = keccak256(
abi.encode(
msg.value,
_destinations,
_inputAsset,
_outputAsset,
_amount,
_amountOutMin,
_ttl,
_data,
_feeParams.fee,
_feeParams.deadline
)
);
_verifySignature(_sigData, _feeParams.sig);
_handleFees(_feeParams.fee, msg.value, _inputAsset, _feeParams.deadline);
// Approve the spoke contract if needed
_approveSpokeIfNeeded(_inputAsset, _amount);
// Create new intent
(_intentId, _intent) =
spoke.newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data);
// Emit event
emit IntentWithFeesAdded(_intentId, msg.sender.toBytes32(), _feeParams.fee, msg.value);
return (_intentId, _intent);
}
/**
* @notice Internal function to create a new intent
* @param _destinations Array of destination chain IDs
* @param _receiver Address of the receiver on the destination chain
* @param _inputAsset Address of the input asset
* @param _outputAsset Address of the output asset
* @param _amount Amount of input asset to transfer
* @param _amountOutMin Maximum fee in basis points that can be charged
* @param _ttl Time-to-live for the intent
* @param _data Additional data for the intent
* @param _feeParams Fee parameters including fee amount, deadline, and signature
* @return _intentId The ID of the created intent
* @return _intent The created intent object
*/
function _newIntent(
uint32[] memory _destinations,
address _receiver,
address _inputAsset,
address _outputAsset,
uint256 _amount,
uint256 _amountOutMin,
uint48 _ttl,
bytes calldata _data,
IFeeAdapterV2.FeeParams calldata _feeParams
) internal returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
// Send fees to recipient
bytes32 _sigData = keccak256(
abi.encode(
msg.value,
_destinations,
_inputAsset,
_outputAsset,
_amount,
_amountOutMin,
_ttl,
_data,
_feeParams.fee,
_feeParams.deadline
)
);
_verifySignature(_sigData, _feeParams.sig);
_handleFees(_feeParams.fee, msg.value, _inputAsset, _feeParams.deadline);
// Approve the spoke contract if needed
_approveSpokeIfNeeded(_inputAsset, _amount);
// Create new intent
(_intentId, _intent) =
spoke.newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data);
// Emit event
emit IntentWithFeesAdded(_intentId, msg.sender.toBytes32(), _feeParams.fee, msg.value);
return (_intentId, _intent);
}
/**
* @notice Updates the fee recipient
* @param _feeRecipient New recipient
*/
function _updateFeeRecipient(
address _feeRecipient
) internal {
emit FeeRecipientUpdated(_feeRecipient, feeRecipient);
feeRecipient = _feeRecipient;
}
/**
* @notice Updates the fee signer
* @param _feeSigner New signer
*/
function _updateFeeSigner(
address _feeSigner
) internal {
emit FeeSignerUpdated(_feeSigner, feeSigner);
feeSigner = _feeSigner;
}
/**
* @notice Verifies a signature
* @param _dataHash The data of the message
* @param _signature The signature of the message
*/
function _verifySignature(
bytes32 _dataHash,
bytes calldata _signature
) internal {
bytes32 _hash = keccak256(abi.encode(_dataHash, msg.sender, address(this), block.chainid));
if (txExists[_hash]) revert FeeAdapter_SignatureAlreadyUsed();
txExists[_hash] = true;
address _recoveredSigner = ECDSA.recover(MessageHashUtils.toEthSignedMessageHash(_hash), _signature);
if (_recoveredSigner != feeSigner) {
revert FeeAdapter_InvalidSignature();
}
}
/**
* @notice Sends fees to recipient
* @param _tokenFee Amount in transacting asset to send to recipient
* @param _nativeFee Amount in native asset to send to recipient
*/
function _handleFees(
uint256 _tokenFee,
uint256 _nativeFee,
address _inputAsset,
uint256 _deadline
) internal {
// Verify the ttl is valid
if (block.timestamp > _deadline) {
revert FeeAdapter_InvalidDeadline();
}
// Handle token fees if exist
if (_tokenFee > 0) {
_pushTokens(feeRecipient, _inputAsset, _tokenFee);
}
// Handle native tokens
if (_nativeFee > 0) {
Address.sendValue(payable(feeRecipient), _nativeFee);
}
}
/**
* @notice Approves the maximum uint value to the gateway.
* @dev Approving the max reduces gas for following intents.
* @param _asset Asset to approve to spoke.
* @param _minimum Minimum required approval budget.
*/
function _approveSpokeIfNeeded(
address _asset,
uint256 _minimum
) internal {
// Checking if the strategy is default or not
address spender;
IEverclearV2.Strategy _strategy = spoke.strategies(_asset);
if (_strategy == IEverclearV2.Strategy.DEFAULT) spender = address(spoke);
else spender = xerc20Module;
// Approve the spoke contract if needed
IERC20 _token = IERC20(_asset);
uint256 _current = _token.allowance(address(this), spender);
if (_current >= _minimum) {
return;
}
// Approve to 0
if (_current != 0) {
_token.safeDecreaseAllowance(spender, _current);
}
// Approve to max
_token.safeIncreaseAllowance(spender, type(uint256).max);
}
/**
* @notice Transfers tokens from the caller to this contract using Permit2
* @dev Uses the Permit2 contract to transfer tokens with a signature
* @param _asset The token to transfer
* @param _amount The amount to transfer
* @param _permit2Params The permit2 parameters including nonce, deadline, and signature
*/
function _pullWithPermit2(
address _asset,
uint256 _amount,
IEverclearSpokeV6.Permit2Params calldata _permit2Params
) internal {
// Transfer from caller using permit2
PERMIT2.permitTransferFrom(
IPermit2.PermitTransferFrom({
permitted: IPermit2.TokenPermissions({token: IERC20(_asset), amount: _amount}),
nonce: _permit2Params.nonce,
deadline: _permit2Params.deadline
}),
IPermit2.SignatureTransferDetails({to: address(this), requestedAmount: _amount}),
msg.sender,
_permit2Params.signature
);
}
/**
* @notice Pull tokens from the sender to the contract
* @param _sender The address of the sender
* @param _asset The address of the asset
* @param _amount The amount of the asset
*/
function _pullTokens(
address _sender,
address _asset,
uint256 _amount
) internal {
IERC20(_asset).safeTransferFrom(_sender, address(this), _amount);
}
/**
* @notice Push tokens from the contract to the recipient
* @param _recipient The address of the recipient
* @param _asset The address of the asset
* @param _amount The amount of the asset
*/
function _pushTokens(
address _recipient,
address _asset,
uint256 _amount
) internal {
IERC20(_asset).safeTransfer(_recipient, _amount);
}
}
"
},
"../../node_modules/@openzeppelin/contracts/access/Ownable2Step.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
"
},
"../../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"../../node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
"
},
"../../node_modules/@openzeppelin/contracts/utils/Address.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
"
},
"../../node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
"
},
"../../node_modules/@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\
32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\
32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\
" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\
", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
"
},
"src/contracts/common/TypeCasts.sol": {
"content": "// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.25;
/**
* @title TypeCasts
* @notice Library for type casts
*/
library TypeCasts {
// alignment preserving cast
/**
* @notice Cast an address to a bytes32
* @param _addr The address to cast
*/
function toBytes32(
address _addr
) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
// alignment preserving cast
/**
* @notice Cast a bytes32 to an address
* @param _buf The bytes32 to cast
*/
function toAddress(
bytes32 _buf
) internal pure returns (address) {
return address(uint160(uint256(_buf)));
}
}
"
},
"src/interfaces/common/IEverclearV2.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
/**
* @title IEverclear
* @notice Common interface for EverclearHub and EverclearSpoke
*/
interface IEverclearV2 {
/*//////////////////////////////////////////////////////////////
ENUMS
//////////////////////////////////////////////////////////////*/
/**
* @notice Enum representing statuses of an intent
*/
enum IntentStatus {
NONE, // 0
ADDED, // 1
DEPOSIT_PROCESSED, // 2
FILLED, // 3
ADDED_AND_FILLED, // 4
INVOICED, // 5
SETTLED, // 6
SETTLED_AND_MANUALLY_EXECUTED, // 7
UNSUPPORTED, // 8
UNSUPPORTED_RETURNED // 9
}
/**
* @notice Enum representing asset strategies
*/
enum Strategy {
DEFAULT,
XERC20
}
/*///////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/**
* @notice The structure of an intent
* @param initiator The address of the intent initiator
* @param receiver The address of the intent receiver
* @param inputAsset The address of the intent asset on origin
* @param outputAsset The address of the intent asset on destination
* @param origin The origin chain of the intent
* @param destinations The possible destination chains of the intent
* @param nonce The nonce of the intent
* @param timestamp The timestamp of the intent
* @param ttl The time to live of the intent
* @param amount The amount of the intent asset normalized to 18 decimals
* @param amountOutMin The minimum amount of the output asset that the intent solver should return
* @param data The data of the intent
*/
struct Intent {
bytes32 initiator;
bytes32 receiver;
bytes32 inputAsset;
bytes32 outputAsset;
uint32 origin;
uint64 nonce;
uint48 timestamp;
uint48 ttl;
uint256 amount;
uint256 amountOutMin;
uint32[] destinations;
bytes data;
}
/**
* @notice The structure of a fill message
* @param intentId The ID of the intent
* @param solver The address of the intent solver in bytes32 format
* @param initiator The address of the intent initiator
* @param fee The total fee of the expressed in dbps, represents the solver fee plus the sum of protocol fees for the token
* @param executionTimestamp The execution timestamp of the intent
*/
struct DeprecatedFillMessage {
bytes32 intentId;
bytes32 solver;
bytes32 initiator;
uint24 fee;
uint48 executionTimestamp;
}
/**
* @notice The structure of a fill message
* @param intentId The ID of the intent
* @param receiver The address of the intent receiver in bytes32 format
* @param intentInputAsset The input asset of the intent (i.e. asset the solver will be repaid in)
* @param intentOrigin The origin chain of the intent
* @param amountOut The amount being sent to the user by the solver
* @param destinations The settlement destinations for the fill
* @param executionTimestamp The execution timestamp of the intent
*/
struct FillMessage {
bytes32 intentId;
bytes32 receiver;
bytes32 intentInputAsset;
uint32 intentOrigin;
uint256 amountOut;
uint32[] destinations;
uint48 executionTimestamp;
}
/**
* @notice The structure of a settlement
* @param intentId The ID of the intent
* @param amount The amount of the asset
* @param asset The address of the asset
* @param recipient The address of the recipient
* @param updateVirtualBalance If set to true, the settlement will not be transferred to the recipient in spoke domain and the virtual balance will be increased
*/
struct Settlement {
bytes32 intentId;
uint256 amount;
bytes32 asset;
bytes32 recipient;
bool updateVirtualBalance;
}
}
"
},
"src/interfaces/common/IPermit2.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
/**
* @title IPermit2
* @notice Interface for permit2
*/
interface IPermit2 {
/*///////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Struct for token and amount in a permit message
* @param token The token to transfer
* @param amount The amount to transfer
*/
struct TokenPermissions {
IERC20 token;
uint256 amount;
}
/**
* @notice Struct for the permit2 message
* @param permitted The permitted token and amount
* @param nonce The unique identifier for this permit
* @param deadline The expiration for this permit
*/
struct PermitTransferFrom {
TokenPermissions permitted;
uint256 nonce;
uint256 deadline;
}
/**
* @notice Struct for the transfer details for permitTransferFrom()
* @param to The recipient of the tokens
* @param requestedAmount The amount to transfer
*/
struct SignatureTransferDetails {
address to;
uint256 requestedAmount;
}
/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Consume a permit2 message and transfer tokens
* @param permit The permit message
* @param transferDetails The transfer details
* @param owner The owner of the tokens
* @param signature The signature of the permit
*/
function permitTransferFrom(
PermitTransferFrom calldata permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes calldata signature
) external;
}
"
},
"src/interfaces/intent/IEverclearSpokeV6.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {IEverclearV2} from 'interfaces/common/IEverclearV2.sol';
import {ISettlementModule} from 'interfaces/common/ISettlementModule.sol';
import {ISpokeStorageV6} from './ISpokeStorageV6.sol';
/**
* @title IEverclearSpoke
* @notice Interface for the EverclearSpoke contract
*/
interface IEverclearSpokeV6 is ISpokeStorageV6 {
/*///////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Parameters needed to execute a permit2
* @param nonce The nonce of the permit
* @param deadline The deadline of the permit
* @param signature The signature of the permit
*/
struct Permit2Params {
uint256 nonce;
uint256 deadline;
bytes signature;
}
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/**
* @notice emitted when a new intent is added on origin
* @param _intentId The ID of the intent
* @param _queueIdx The index of the intent in the IntentQueue
* @param _intent The intent object
*/
event IntentAdded(bytes32 indexed _intentId, uint256 _queueIdx, Intent _intent);
/**
* @notice emitted when an intent is filled on destination
* @param _intentId The ID of the intent
* @param _solver The address of the intent solver
* @param _receiver The address of the intent receiver
* @param _amountOut The total amount the user has been transferred
* @param _queueIdx The index of the FillMessage in the FillQueue
* @param _intent The full intent object
*/
event IntentFilled(
bytes32 indexed _intentId,
address indexed _solver,
bytes32 indexed _receiver,
uint256 _amountOut,
uint256 _queueIdx,
Intent _intent
);
/**
* @notice emitted when solver (or anyone) deposits an asset in the EverclearSpoke
* @param _depositant The address of the depositant
* @param _asset The address of the deposited asset
* @param _amount The amount of the deposited asset
*/
event Deposited(address indexed _depositant, address indexed _asset, uint256 _amount);
/**
* @notice emitted when solver (or anyone) withdraws an asset from the EverclearSpoke
* @param _withdrawer The address of the withdrawer
* @param _asset The address of the withdrawn asset
* @param _amount The amount of the withdrawn asset
*/
event Withdrawn(address indexed _withdrawer, address indexed _asset, uint256 _amount);
/**
* @notice Emitted when the intent queue is processed
* @param _messageId The ID of the message
* @param _firstIdx The first index of the queue to be processed
* @param _lastIdx The last index of the queue to be processed
* @param _quote The quote amount
*/
event IntentQueueProcessed(bytes32 indexed _messageId, uint256 _firstIdx, uint256 _lastIdx, uint256 _quote);
/**
* @notice Emitted when the fill queue is processed
* @param _messageId The ID of the message
* @param _firstIdx The first index of the queue to be processed
* @param _lastIdx The last index of the queue to be processed
* @param _quote The quote amount
*/
event FillQueueProcessed(bytes32 indexed _messageId, uint256 _firstIdx, uint256 _lastIdx, uint256 _quote);
/**
* @notice Emitted when an external call is executed
* @param _intentId The ID of the intent
* @param _returnData The return data of the call
*/
event ExternalCalldataExecuted(bytes32 indexed _intentId, bytes _returnData);
/**
* @notice Emitted when feeAdapter is updated
* @param _newFeeAdapter The new fee adapter
*/
event FeeAdapterUpdated(address _newFeeAdapter);
/**
* @notice Emitted when fill signer is updated
* @param _oldFillSigner The old fill signer
* @param _newFillSigner The new fill signer
*/
event FillSignerUpdated(address _oldFillSigner, address _newFillSigner);
/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/**
* @notice Thrown when the intent is already filled
* @param _intentId The id of the intent which is being tried to fill
*/
error EverclearSpoke_FillIntent_InvalidStatus(bytes32 _intentId);
/**
* @notice Thrown when trying to fill an expired intent
* @param _intentId The id of the intent which is being tried to fill
*/
error EverclearSpoke_FillIntent_IntentExpired(bytes32 _intentId);
/**
* @notice Thrown when calling newIntent with invalid intent parameters
*/
error EverclearSpoke_NewIntent_InvalidIntent();
/**
* @notice Thrown when the ttl is non-zero and outputAsset is null
*/
error EverclearSpoke_NewIntent_OutputAssetNull();
/**
* @notice Thrown when the destination array > 1 and outputAsset is not null
*/
error EverclearSpoke_NewIntent_OutputAssetNotNull();
/**
* @notice Thrown when the maxFee is exceeded
* @param _amountOut The amount sent by the solver
* @param _amountOutMin The min amount out
*/
error EverclearSpoke_FillIntent_AmountOutInvalid(uint256 _amountOut, uint256 _amountOutMin);
/**
* @notice Thrown when the intent amount is zero
*/
error EverclearSpoke_NewIntent_ZeroAmount();
/**
* @notice Thrown when the solver doesnt have sufficient funds to fill an intent
* @param _requested The amount of tokens needed to fill the intent
* @param _available The amount of tokens the solver has deposited in the `EverclearSpoke`
*/
error EverclearSpoke_FillIntent_InsufficientFunds(uint256 _requested, uint256 _available);
/**
* @notice Thrown when the destination array is empty
*/
error EverclearSpoke_FillIntent_InvalidDestinationArray();
/**
* @notice Thrown when the intent calldata exceeds the limit
*/
error EverclearSpoke_NewIntent_CalldataExceedsLimit();
/**
* @notice Thrown when a signature signer does not match the expected address
*/
error EverclearSpoke_InvalidSignature();
/**
* @notice Thrown when the domain does not match the expected domain
*/
error EverclearSpoke_ProcessFillViaRelayer_WrongDomain();
/**
* @notice Thrown when the relayer address does not match the msg.sender
*/
error EverclearSpoke_ProcessFillViaRelayer_NotRelayer();
/**
* @notice Thrown when the TTL of the message has expired
*/
error EverclearSpoke_ProcessFillViaRelayer_TTLExpired();
/**
* @notice Thrown when processing the intent queue and the intent is not found in the position specified in the parameter
* @param _intentId The id of the intent being processed
* @param _position The position specified by the queue processor
*/
error EverclearSpoke_ProcessIntentQueue_NotFound(bytes32 _intentId, uint256 _position);
/**
* @notice Thrown when trying to execute the calldata of an intent with invalid status
* @param _intentId The id of the intent whose calldata is trying to be executed
*/
error EverclearSpoke_ExecuteIntentCalldata_InvalidStatus(bytes32 _intentId);
/**
* @notice Thrown when the external call failed on executeIntentCalldata
*/
error EverclearSpoke_ExecuteIntentCalldata_ExternalCallFailed();
/**
* @notice Thrown when the queues are non-empty
*/
error EverclearSpoke_Initialize_IntentQueueNotEmpty();
/**
* @notice Thrown when the queues are non-empty
*/
error EverclearSpoke_Initialize_FillQueueNotEmpty();
/**
* @notice Thrown when the array length invalid in a batch fill
*/
error EverclearSpoke_FillIntent_InvalidArrayLengths();
/**
* @notice Thrown when the fill signature is invalid
*/
error EverclearSpoke_InvalidFillSignature();
/**
* @notice Thrown when the dynamic gas limit exceeds the maximum gas limit
* @param _dynamicGasLimit The dynamic gas limit being provided
*/
error EverclearSpoke_ProcessQueue_ExceedsGasLimit(uint256 _dynamicGasLimit);
/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Pauses the contract
* @dev only the lighthouse and watchtower can pause the contract
*/
function pause() external;
/**
* @notice Unpauses the contract
* @dev only the lighthouse and watchtower can unpause the contract
*/
function unpause() external;
/**
* @notice Sets a minting / burning strategy for an asset
* @param _asset The asset address
* @param _strategy The strategy id (see `enum Strategy`)
*/
function setStrategyForAsset(
address _asset,
IEverclearV2.Strategy _strategy
) external;
/**
* @notice Sets a module for a strategy
* @param _strategy The strategy id (see `enum Strategy`)
* @param _module The module contract
*/
function setModuleForStrategy(
IEverclearV2.Strategy _strategy,
ISettlementModule _module
) external;
/**
* @notice Updates the security module
* @param _newSecurityModule The address of the new security module
*/
function updateSecurityModule(
address _newSecurityModule
) external;
/**
* @notice Update the fee adapter
* @param _newFeeAdapter The address of the new fee adapter
*/
function updateFeeAdapter(
address _newFeeAdapter
) external;
/**
* @notice Initialize the EverclearSpoke contract
*/
function initialize(
address _feeAdapter,
address _messageReceiver,
address _fillSigner
) external;
/**
* @notice Creates a new intent
* @param _destinations The possible destination chains of the intent
* @param _receiver The destination address of the intent
* @param _inputAsset The asset address on origin
* @param _outputAsset The asset address on destination
* @param _amount The amount of the asset
* @param _amountOutMin The minimum amount out the solver should return
* @param _ttl The time to live of the intent
* @param _data The data of the intent
* @return _intentId The ID of the intent
* @return _intent The intent object
*/
function newIntent(
uint32[] memory _destinations,
bytes32 _receiver,
address _inputAsset,
bytes32 _outputAsset,
uint256 _amount,
uint256 _amountOutMin,
uint48 _ttl,
bytes calldata _data
) external returns (bytes32 _intentId, Intent memory _intent);
/**
* @notice Creates a new intent
* @param _destinations The possible destination chains of the intent
* @param _receiver The destination address of the intent
* @param _inputAsset The asset address on origin
* @param _outputAsset The asset address on destination
* @param _amount The amount of the asset
* @param _amountOutMin The minimum amount out the solver should return
* @param _ttl The time to live of the intent
* @param _data The data of the intent
* @return _intentId The ID of the intent
* @return _intent The intent object
*/
function newIntent(
uint32[] memory _destinations,
address _receiver,
address _inputAsset,
addr
Submitted on: 2025-10-29 16:32:11
Comments
Log in to comment.
No comments yet.