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/swapperEngine/SwapperEngine.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20Permit} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Permit.sol";
import {PausableUpgradeable} from "openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
import {ReentrancyGuardUpgradeable} from
"openzeppelin-contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {IRegistryContract} from "src/interfaces/registry/IRegistryContract.sol";
import {IRegistryAccess} from "src/interfaces/registry/IRegistryAccess.sol";
import {ISwapperEngine} from "src/interfaces/swapperEngine/ISwapperEngine.sol";
import {IEur0} from "src/interfaces/token/IEur0.sol";
import {IOracle} from "src/interfaces/oracles/IOracle.sol";
import {Normalize} from "src/utils/normalize.sol";
import {CheckAccessControl} from "src/utils/CheckAccessControl.sol";
import {
ONE_EURC,
DEFAULT_ADMIN_ROLE,
PAUSING_CONTRACTS_ROLE,
UNPAUSING_CONTRACTS_ROLE,
CONTRACT_REGISTRY_ACCESS,
CONTRACT_ORACLE,
EURC,
CONTRACT_EUR0,
MINIMUM_EURC_PROVIDED
} from "src/constants.sol";
import {
InsufficientEUR0Balance,
OrderNotActive,
NoOrdersIdsProvided,
NotRecipient,
AmountTooLow,
AmountIsZero,
NotAuthorized,
NullContract,
NullAddress
} from "src/errors.sol";
/// @title SwapperEngine
/// @notice A contract for swapping EURC tokens for EUR0 tokens using order matching.
/// @dev This contract allows users to deposit EURC tokens, create orders, and match those orders
/// against EUR0 tokens provided by other users.
/// Order matching works by allowing users to create individual orders specifying the amount
/// of EURC tokens they wish to swap. Other users can then provide EUR0 tokens to match against
/// these orders, directly swapping their EUR0 tokens for the EURC tokens in the orders.
/// Tradeoffs:
/// + Direct swaps: Users can directly swap their tokens with each other, without the need for
/// intermediary liquidity pools.
/// + Low Slippage: The price of the swap is determined by the eurc oracle price at that block height irrespective of liquidity depth.
/// - Liquidity: Order matching relies on the presence of active orders to facilitate swaps.
/// If there are no matching orders available, users may need to wait for new orders to be created.
/// - Match to Market: The price of the swaps is determined by the individual orders when they are executed not when they are placed (offset by low price volatility of stables)
/// @custom:mechanism Effectively this facilitates RWA --> EUR0 --> EURC --> EUR0 --> RWA ... limited only by EURC orderbook depth
/// @author Usual Tech team
contract SwapperEngine is
Initializable,
ReentrancyGuardUpgradeable,
PausableUpgradeable,
ISwapperEngine
{
using SafeERC20 for IERC20;
using SafeERC20 for IEur0;
using Normalize for uint256;
using CheckAccessControl for IRegistryAccess;
struct SwapperEngineStorageV0 {
IRegistryAccess registryAccess;
IRegistryContract registryContract;
IOracle oracle;
IERC20 eurcToken;
IEur0 eur0;
mapping(uint256 => EurcOrder) orders;
uint256 nextOrderId;
uint256 minimumEURCAmountProvided;
}
// keccak256(abi.encode(uint256(keccak256("swapperengine.storage.v0")) - 1)) & ~bytes32(uint256(0xff))
// solhint-disable-next-line
bytes32 public constant SwapperEngineStorageV0Location =
0x6c3529a15b63e79e1691946ad3dcd9eb824ac76513a1aed382fd5661938dea00;
/// @notice Returns the storage struct of the contract.
/// @return $ .
function _swapperEngineStorageV0() internal pure returns (SwapperEngineStorageV0 storage $) {
bytes32 position = SwapperEngineStorageV0Location;
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := position
}
}
/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Function for initializing the contract.
/// @dev This function is used to set the initial state of the contract.
/// @param registryContract The registry contract address.
function initialize(address registryContract) public initializer {
if (registryContract == address(0)) {
revert NullContract();
}
__Pausable_init_unchained();
__ReentrancyGuard_init_unchained();
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
$.registryContract = IRegistryContract(registryContract);
$.registryAccess = IRegistryAccess($.registryContract.getContract(CONTRACT_REGISTRY_ACCESS));
$.eurcToken = IERC20(EURC);
$.eur0 = IEur0($.registryContract.getContract(CONTRACT_EUR0));
$.nextOrderId = 1;
$.oracle = IOracle($.registryContract.getContract(CONTRACT_ORACLE));
$.minimumEURCAmountProvided = MINIMUM_EURC_PROVIDED;
}
/*//////////////////////////////////////////////////////////////
Restricted Functions
//////////////////////////////////////////////////////////////*/
/// @inheritdoc ISwapperEngine
function updateMinimumEURCAmountProvided(uint256 minimumEURCAmount) external {
if (minimumEURCAmount < ONE_EURC) {
// Minimum amount must be at least 1 EURC
revert AmountTooLow();
}
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
$.registryAccess.onlyMatchingRole(DEFAULT_ADMIN_ROLE);
$.minimumEURCAmountProvided = minimumEURCAmount;
}
/// @inheritdoc ISwapperEngine
function pause() external {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
$.registryAccess.onlyMatchingRole(PAUSING_CONTRACTS_ROLE);
_pause();
}
/// @inheritdoc ISwapperEngine
function unpause() external {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
$.registryAccess.onlyMatchingRole(UNPAUSING_CONTRACTS_ROLE);
_unpause();
}
/*//////////////////////////////////////////////////////////////
Internal / Private Functions
//////////////////////////////////////////////////////////////*/
///@notice Retrieves the current price of EURC in WAD format (18 decimals).
///@dev This function fetches the price of EURC from the oracle contract and returns it in WAD format.
///@return eurcWadPrice The current price of EURC in WAD format.
function _getEurcWadPrice() private view returns (uint256 eurcWadPrice) {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
eurcWadPrice = uint256($.oracle.getPrice(address($.eurcToken)));
}
///@notice Calculates the EUR0 equivalent amount in WAD format for a given EURC token amount.
///@dev This function converts the EURC token amount from its native decimal representation to WAD format (18 decimals),
/// and then calculates the equivalent EUR0 amount based on the provided EURC price in WAD format.
///@param eurcTokenAmountInNativeDecimals The amount of EURC tokens in their native decimal representation (6 decimals).
///@param eurcWadPrice The price of EURC in WAD format.
///@return eur0WadEquivalent The equivalent amount of EUR0 in WAD format.
function _getEur0WadEquivalent(uint256 eurcTokenAmountInNativeDecimals, uint256 eurcWadPrice)
private
view
returns (uint256 eur0WadEquivalent)
{
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
uint8 decimals = IERC20Metadata(address($.eurcToken)).decimals();
uint256 eurcWad = eurcTokenAmountInNativeDecimals.tokenAmountToWad(decimals);
eur0WadEquivalent = eurcWad.wadAmountByPrice(eurcWadPrice);
}
/// @notice Calculates the EURC token amount in native decimals for a given EUR0 amount in WAD format.
/// @dev This function calculates the expected EURC amount to receive based on the provided eur0WadAmount and EURC price in WAD format.
/// @param eur0WadAmount The amount of EUR0 in WAD format.
/// @param eurcWadPrice The price of EURC in WAD format.
/// @return eurcTokenAmountInNativeDecimals The equivalent amount of EURC tokens in their native decimal representation.
function _getEurcAmountFromEur0WadEquivalent(uint256 eur0WadAmount, uint256 eurcWadPrice)
private
view
returns (uint256 eurcTokenAmountInNativeDecimals)
{
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
uint8 decimals = IERC20Metadata(address($.eurcToken)).decimals();
eurcTokenAmountInNativeDecimals =
eur0WadAmount.wadTokenAmountForPrice(eurcWadPrice, decimals);
}
function _depositEURC(
SwapperEngineStorageV0 storage $,
uint256 amountToDeposit,
address sender,
address recipient
) internal {
if (amountToDeposit < $.minimumEURCAmountProvided) {
// amountToDeposit must be equal or greater than the minimumEURCAmountProvided storage value
revert AmountTooLow();
}
if (recipient == address(0)) {
revert NullAddress();
}
if ($.eur0.isBlacklisted(sender) || $.eur0.isBlacklisted(recipient)) {
revert NotAuthorized();
}
uint256 orderId = $.nextOrderId++;
$.orders[orderId] =
EurcOrder({recipient: recipient, tokenAmount: amountToDeposit, active: true});
// Transfer EURC tokens to this contract
$.eurcToken.safeTransferFrom(sender, address(this), amountToDeposit);
emit Deposit(sender, recipient, orderId, amountToDeposit);
}
/// @notice Allows a user to provide EUR0 tokens and receive EURC tokens by matching against existing orders.
/// @dev This function allows users to specify an amount of EURC tokens they want, calculating the corresponding
/// EUR0 tokens they need and exchanging it against active orders.
/// @param recipient The address to receive the EUR0 tokens.
/// @param amountEurcToTakeInNativeDecimals The amount of EURC tokens to take, in the token's native decimal representation.
/// @param orderIdsToTake An array of order IDs to match against.
/// @param partialMatchingAllowed A flag indicating whether partial matching is allowed.
/// @return unmatchedEurcAmount The amount of EURC tokens that were not matched.
/// @return totalEur0Provided The total amount of EUR0 tokens provided.
function _provideEur0ReceiveEURC( // solhint-disable-line
address recipient,
uint256 amountEurcToTakeInNativeDecimals,
uint256[] memory orderIdsToTake,
bool partialMatchingAllowed,
uint256 eurcWadPrice
) internal returns (uint256 unmatchedEurcAmount, uint256 totalEur0Provided) {
if (amountEurcToTakeInNativeDecimals == 0) {
// Amount must be greater than 0
revert AmountIsZero();
}
if (orderIdsToTake.length == 0) {
revert NoOrdersIdsProvided();
}
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
uint256 totalEurcTaken = 0;
for (
uint256 i;
i < orderIdsToTake.length && totalEurcTaken < amountEurcToTakeInNativeDecimals;
) {
uint256 orderId = orderIdsToTake[i];
EurcOrder storage order = $.orders[orderId];
if (order.active) {
uint256 remainingAmountToTake = amountEurcToTakeInNativeDecimals - totalEurcTaken;
// if the eurcOrder tokenAmount > remainingAmountToTake only take the remaining else take the whole order
uint256 amountOfEurcFromOrder = order.tokenAmount > remainingAmountToTake
? remainingAmountToTake
: order.tokenAmount;
// @NOTE oracle price check & calculation of nominal EURC TokenAmount to EUR in 18 decimals.
// EUR0 has 18 decimals and we are considering it with a static EUR value of 1.
// EURC has 6 decimals, needs to be normalized to 18 decimals.
order.tokenAmount -= amountOfEurcFromOrder;
totalEurcTaken += amountOfEurcFromOrder;
if (order.tokenAmount == 0) {
order.active = false;
}
uint256 eur0Amount = _getEur0WadEquivalent(amountOfEurcFromOrder, eurcWadPrice);
totalEur0Provided += eur0Amount;
// Transfer EUR0 from sender to order recipient
$.eur0.safeTransferFrom(msg.sender, order.recipient, eur0Amount);
emit OrderMatched(order.recipient, msg.sender, orderId, amountOfEurcFromOrder);
}
unchecked {
++i;
}
}
// Transfer EURC from this contract to the recipient
$.eurcToken.safeTransfer(recipient, totalEurcTaken);
// Revert if partial matching is not allowed and we haven't taken all of the EUR0
if (
!partialMatchingAllowed && totalEurcTaken != amountEurcToTakeInNativeDecimals
|| totalEurcTaken == 0
) {
revert AmountTooLow();
}
return ((amountEurcToTakeInNativeDecimals - totalEurcTaken), totalEur0Provided);
}
/*//////////////////////////////////////////////////////////////
External / Public Functions
//////////////////////////////////////////////////////////////*/
/// @inheritdoc ISwapperEngine
function depositEURC(uint256 amountToDeposit) external nonReentrant whenNotPaused {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
_depositEURC($, amountToDeposit, msg.sender, msg.sender);
}
/// @inheritdoc ISwapperEngine
function depositEURCOnBehalf(uint256 amountToDeposit, address recipient)
external
nonReentrant
whenNotPaused
{
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
_depositEURC($, amountToDeposit, msg.sender, recipient);
}
/// @inheritdoc ISwapperEngine
function depositEURCWithPermit(
uint256 amountToDeposit,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
try IERC20Permit(address($.eurcToken)).permit(
msg.sender, address(this), amountToDeposit, deadline, v, r, s
) {} catch {} // solhint-disable-line no-empty-blocks
_depositEURC($, amountToDeposit, msg.sender, msg.sender);
}
/// @inheritdoc ISwapperEngine
function withdrawEURC(uint256 orderToCancel) external nonReentrant whenNotPaused {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
EurcOrder storage order = $.orders[orderToCancel];
if (!order.active) {
// Order not active or does not exist
revert OrderNotActive();
}
if (order.recipient != msg.sender) {
// Only the recipient can cancel their order
revert NotRecipient();
}
uint256 amountToWithdraw = order.tokenAmount;
order.active = false; // Deactivate the order
order.tokenAmount = 0; // Set the amount to zero
// Transfer EURC back to the recipient
$.eurcToken.safeTransfer(order.recipient, amountToWithdraw);
emit Withdraw(order.recipient, orderToCancel, amountToWithdraw);
}
/// @inheritdoc ISwapperEngine
function provideEur0ReceiveEURC(
address recipient,
uint256 amountEurcToTakeInNativeDecimals,
uint256[] memory orderIdsToTake,
bool partialMatchingAllowed
) external nonReentrant whenNotPaused returns (uint256) {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
uint256 eurcWadPrice = _getEurcWadPrice();
uint256 requiredEur0Amount =
_getEur0WadEquivalent(amountEurcToTakeInNativeDecimals, eurcWadPrice);
if ($.eur0.balanceOf(msg.sender) < requiredEur0Amount) {
revert InsufficientEUR0Balance();
}
(uint256 unmatchedEurcAmount,) = _provideEur0ReceiveEURC(
recipient,
amountEurcToTakeInNativeDecimals,
orderIdsToTake,
partialMatchingAllowed,
eurcWadPrice
);
return unmatchedEurcAmount;
}
/// @inheritdoc ISwapperEngine
function provideEur0ReceiveEURCWithPermit(
address recipient,
uint256 amountEurcToTakeInNativeDecimals,
uint256[] memory orderIdsToTake,
bool partialMatchingAllowed,
uint256 eur0ToPermit,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused returns (uint256) {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
uint256 eurcWadPrice = _getEurcWadPrice();
uint256 requiredEur0Amount =
_getEur0WadEquivalent(amountEurcToTakeInNativeDecimals, eurcWadPrice);
// Authorization transfer
if ($.eur0.balanceOf(msg.sender) < requiredEur0Amount || eur0ToPermit < requiredEur0Amount)
{
revert InsufficientEUR0Balance();
}
try IERC20Permit(address($.eur0)).permit(
msg.sender, address(this), eur0ToPermit, deadline, v, r, s
) {} catch {} // solhint-disable-line no-empty-blocks
(uint256 unmatchedEurcAmount,) = _provideEur0ReceiveEURC(
recipient,
amountEurcToTakeInNativeDecimals,
orderIdsToTake,
partialMatchingAllowed,
eurcWadPrice
);
return unmatchedEurcAmount;
}
/// @inheritdoc ISwapperEngine
function swapEur0(
address recipient,
uint256 amountEur0ToProvideInWad,
uint256[] memory orderIdsToTake,
bool partialMatchingAllowed
) external nonReentrant whenNotPaused returns (uint256) {
uint256 eurcWadPrice = _getEurcWadPrice();
(, uint256 totalEur0Provided) = _provideEur0ReceiveEURC(
recipient,
_getEurcAmountFromEur0WadEquivalent(amountEur0ToProvideInWad, eurcWadPrice),
orderIdsToTake,
partialMatchingAllowed,
eurcWadPrice
);
return amountEur0ToProvideInWad - totalEur0Provided;
}
/*//////////////////////////////////////////////////////////////
Getter Functions
//////////////////////////////////////////////////////////////*/
/// @inheritdoc ISwapperEngine
function minimumEURCAmountProvided() public view returns (uint256 minimumEURCAmount) {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
minimumEURCAmount = $.minimumEURCAmountProvided;
}
/// @inheritdoc ISwapperEngine
function getOrder(uint256 orderId) public view returns (bool active, uint256 tokenAmount) {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
EurcOrder memory order = $.orders[orderId];
active = order.active;
tokenAmount = order.tokenAmount;
}
/// @inheritdoc ISwapperEngine
function getNextOrderId() external view override returns (uint256) {
SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
return $.nextOrderId;
}
}
"
},
"lib/openzeppelin-contracts/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);
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}
"
},
"lib/openzeppelin-contracts/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;
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}
"
},
"src/interfaces/registry/IRegistryContract.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
interface IRegistryContract {
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @notice This event is emitted when the address of the contract is set
/// @param name The name of the contract in bytes32
/// @param contractAddress The address of the contract
event SetContract(bytes32 indexed name, address indexed contractAddress);
/*//////////////////////////////////////////////////////////////
Functions
//////////////////////////////////////////////////////////////*/
/// @notice Set the address of the contract
/// @param name The name of the contract in bytes32
/// @param contractAddress The address of the contract
function setContract(bytes32 name, address contractAddress) external;
/// @notice Get the address of the contract
/// @param name The name of the contract in bytes32
/// @return contractAddress The address of the contract
function getContract(bytes32 name) external view returns (address);
}
"
},
"src/interfaces/registry/IRegistryAccess.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import {IAccessControlDefaultAdminRules} from
"openzeppelin-contracts/access/extensions/IAccessControlDefaultAdminRules.sol";
interface IRegistryAccess is IAccessControlDefaultAdminRules {
/*//////////////////////////////////////////////////////////////
Functions
//////////////////////////////////////////////////////////////*/
/// @notice Set the admin role for a specific role
/// @param role The role to set the admin for
/// @param adminRole The admin role to set
function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
}
"
},
"src/interfaces/swapperEngine/ISwapperEngine.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
interface ISwapperEngine {
/*//////////////////////////////////////////////////////////////
Structs
//////////////////////////////////////////////////////////////*/
/// @notice Struct representing a EURC order.
struct EurcOrder {
/// @dev The amount of EURC tokens associated with the order.
uint256 tokenAmount;
/// @dev The address of the recipient.
address recipient;
/// @dev A boolean indicating whether the order is active or not.
bool active;
}
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @notice Event emitted when an order is deposited.
/// @param requester The address of the requester.
/// @param recipient The address of the recipient.
/// @param orderId The ID of the order.
/// @param amount The amount of EURC tokens deposited.
event Deposit(
address indexed requester,
address indexed recipient,
uint256 indexed orderId,
uint256 amount
);
/// @notice Event emitted when an order is withdrawn.
/// @param recipient The address of the recipient.
/// @param orderId The ID of the order.
/// @param amount The amount of EURC tokens withdrawn.
event Withdraw(address indexed recipient, uint256 indexed orderId, uint256 amount);
/// @notice Event emitted when an order is matched.
/// @param eurcProviderAddr The address of the EURC provider.
/// @param eur0Provider The address of the EUR0 provider.
/// @param orderId The ID of the order.
/// @param amount The amount of EURC tokens matched.
event OrderMatched(
address indexed eurcProviderAddr,
address indexed eur0Provider,
uint256 indexed orderId,
uint256 amount
);
/*//////////////////////////////////////////////////////////////
Functions
//////////////////////////////////////////////////////////////*/
/// @notice Returns the minimum EURC amount required for providing liquidity.
/// @return minimumEURCAmount The minimum EURC amount required for providing liquidity.
function minimumEURCAmountProvided() external view returns (uint256);
/// @notice Retrieves the details of a specific EURC order.
/// @dev This function returns the active status and token amount of the specified order.
/// @param orderId The unique identifier of the order.
/// @return active A boolean indicating whether the order is active or not.
/// @return tokenAmount The amount of EURC tokens associated with the order.
function getOrder(uint256 orderId) external view returns (bool active, uint256 tokenAmount);
/// @notice Updates the minimum amount of EURC that must be provided in a deposit.
/// @dev This function can only be called by an administrator.
/// @param minimumEURCAmount The new minimum amount of EURC to deposit
/// The new minimumEURCAmount must be greater than 1 EURC (1e6)
function updateMinimumEURCAmountProvided(uint256 minimumEURCAmount) external;
/// @notice Pauses the contract.
/// @dev This function can only be called by a pauser.
function pause() external;
/// @notice Unpauses the contract.
/// @dev This function can only be called by an unpauser.
function unpause() external;
/// @notice Allows a user to deposit EURC tokens and create a new order.
/// @dev This function transfers the specified amount of EURC tokens from the caller to the contract
/// and creates a new order with the deposited amount and the caller as the requester.
/// @param amountToDeposit The amount of EURC tokens to deposit.
function depositEURC(uint256 amountToDeposit) external;
/// @notice Allows a user to deposit EURC tokens and create a new order on behalf of another address.
/// @dev This function transfers the specified amount of EURC tokens from the caller to the contract
/// and creates a new order with the deposited amount and the caller as the requester.
/// @param amountToDeposit The amount of EURC tokens to deposit.
/// @param recipient The address to receive the EURC tokens.
function depositEURCOnBehalf(uint256 amountToDeposit, address recipient) external;
/// @notice Allows a user to deposit EURC tokens with permit and create a new order.
/// @dev This function transfers the specified amount of EURC tokens from the caller to the contract
/// and creates a new order with the deposited amount and the caller as the requester.
/// @param amountToDeposit The amount of EURC tokens to deposit.
/// @param deadline The deadline for the permit
/// @param v The v value for the permit
/// @param r The r value for the permit
/// @param s The s value for the permit
function depositEURCWithPermit(
uint256 amountToDeposit,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/// @notice Allows the recipient of an order to withdraw their deposited EURC tokens and cancel the order.
/// @dev This function deactivates the specified order, sets its token amount to zero, and transfers
/// the deposited EURC tokens back to the recipient.
/// @param orderToCancel The ID of the order to cancel and withdraw from.
function withdrawEURC(uint256 orderToCancel) external;
/// @notice Allows a user to provide EUR0 tokens and receive EURC tokens by matching against existing orders.
/// @dev This function allows users to specify an amount of EURC tokens they want, calculating the corresponding
/// EUR0 tokens they need and exchanging it against active orders.
/// @param recipient The address to receive the EURC tokens.
/// @param amountEurcToTakeInNativeDecimals The amount of EURC tokens to take, in the token's native decimal representation.
/// @param orderIdsToTake An array of order IDs to match against.
/// @param partialMatchingAllowed A flag indicating whether partial matching is allowed.
/// @return The unmatched amount of EURC tokens.
function provideEur0ReceiveEURC(
address recipient,
uint256 amountEurcToTakeInNativeDecimals,
uint256[] memory orderIdsToTake,
bool partialMatchingAllowed
) external returns (uint256);
/// @notice provideEur0ReceiveEURCWithPermit method with permit
/// @dev This function allows users to to swap their EUR0 for EURC with permit
/// @param recipient The address to receive the EURC tokens.
/// @param amountEurcToTakeInNativeDecimals The amount of EURC tokens to take, in the token's native decimal representation.
/// @param orderIdsToTake An array of order IDs to match against.
/// @param partialMatchingAllowed A flag indicating whether partial matching is allowed.
/// @param deadline The deadline for the permit
/// @param eur0ToPermit The amount of EUR0 tokens to permit, must be greater than the equivalent amount of EURC tokens to take.
/// @param v The v value for the permit
/// @param r The r value for the permit
/// @param s The s value for the permit
/// @return The unmatched amount of EURC tokens.
function provideEur0ReceiveEURCWithPermit(
address recipient,
uint256 amountEurcToTakeInNativeDecimals,
uint256[] memory orderIdsToTake,
bool partialMatchingAllowed,
uint256 eur0ToPermit,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256);
/// @notice Allows users to specify an amount of EUR0 tokens to swap and receive EURC tokens by matching against existing orders.
/// @dev This function handles the precision differences between EUR0 and EURC taking dust into account
/// to ensure accurate conversion. It returns the unmatched EUR0 amount in WAD format, including the dust.
/// @param recipient The address to receive the EURC tokens.
/// @param amountEur0ToProvideInWad The amount of EUR0 to provide in WAD format.
/// @param orderIdsToTake An array of order IDs to match against.
/// @param partialMatchingAllowed A flag indicating whether partial matching is allowed.
/// @return The unmatched amount of EUR0 tokens in WAD format.
function swapEur0(
address recipient,
uint256 amountEur0ToProvideInWad,
uint256[] memory orderIdsToTake,
bool partialMatchingAllowed
) external returns (uint256);
/// @notice Get the next order ID.
/// @dev This function returns the next order ID, which is the total number of orders created.
/// @return The next order ID.
function getNextOrderId() external view returns (uint256);
}
"
},
"src/interfaces/token/IEur0.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IEur0 is IERC20Metadata {
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @notice Event emitted when an address is blacklisted.
/// @param account The address that was blacklisted.
event Blacklist(address account);
/// @notice Event emitted when an address is removed from blacklist.
/// @param account The address that was removed from blacklist.
event UnBlacklist(address account);
/*//////////////////////////////////////////////////////////////
Functions
//////////////////////////////////////////////////////////////*/
/// @notice Pauses all token transfers.
/// @dev Can only be called by an account with the PAUSING_CONTRACTS_ROLE
function pause() external;
/// @notice Unpauses all token transfers.
/// @dev Can only be called by an account with the UNPAUSING_CONTRACTS_ROLE
function unpause() external;
/// @notice mint Eur0 token
/// @dev Can only be called by EUR0_MINT role;
/// @dev Can only mint if enough EUR backing is available.
/// @param to address of the account who want to mint their token
/// @param amount the amount of tokens to mint
function mint(address to, uint256 amount) external;
/// @notice burnFrom Eur0 token
/// @dev Can only be called by EUR0_BURN role
/// @param account address of
Submitted on: 2025-10-03 11:33:32
Comments
Log in to comment.
No comments yet.