Contract Name:
EulerRedemptionBridge
Contract Source Code:
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*
* _Available since v4.7._
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(
uint256 assets,
address receiver,
address owner
) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount
) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @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.
*/
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].
*/
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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../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;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @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, it is bubbled up by this
* function (like regular Solidity function calls).
*
* 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.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @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`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
}
}
}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec
pragma solidity >=0.8.4;
// @dev For documentation of the functions within this interface see RollupProcessor contract
interface IRollupProcessor {
/*----------------------------------------
MUTATING FUNCTIONS
----------------------------------------*/
function pause() external;
function unpause() external;
function setRollupProvider(address _provider, bool _valid) external;
function setVerifier(address _verifier) external;
function setAllowThirdPartyContracts(bool _allowThirdPartyContracts) external;
function setDefiBridgeProxy(address _defiBridgeProxy) external;
function setSupportedAsset(address _token, uint256 _gasLimit) external;
function setSupportedBridge(address _bridge, uint256 _gasLimit) external;
function processRollup(bytes calldata _encodedProofData, bytes calldata _signatures) external;
function receiveEthFromBridge(uint256 _interactionNonce) external payable;
function approveProof(bytes32 _proofHash) external;
function depositPendingFunds(uint256 _assetId, uint256 _amount, address _owner, bytes32 _proofHash)
external
payable;
function offchainData(uint256 _rollupId, uint256 _chunk, uint256 _totalChunks, bytes calldata _offchainTxData)
external;
function processAsyncDefiInteraction(uint256 _interactionNonce) external returns (bool);
/*----------------------------------------
NON-MUTATING FUNCTIONS
----------------------------------------*/
function rollupStateHash() external view returns (bytes32);
function userPendingDeposits(uint256 _assetId, address _user) external view returns (uint256);
function defiBridgeProxy() external view returns (address);
function prevDefiInteractionsHash() external view returns (bytes32);
function paused() external view returns (bool);
function verifier() external view returns (address);
function getDataSize() external view returns (uint256);
function getPendingDefiInteractionHashesLength() external view returns (uint256);
function getDefiInteractionHashesLength() external view returns (uint256);
function getAsyncDefiInteractionHashesLength() external view returns (uint256);
function getSupportedBridge(uint256 _bridgeAddressId) external view returns (address);
function getSupportedBridgesLength() external view returns (uint256);
function getSupportedAssetsLength() external view returns (uint256);
function getSupportedAsset(uint256 _assetId) external view returns (address);
function getEscapeHatchStatus() external view returns (bool, uint256);
function assetGasLimits(uint256 _bridgeAddressId) external view returns (uint256);
function bridgeGasLimits(uint256 _bridgeAddressId) external view returns (uint256);
function allowThirdPartyContracts() external view returns (bool);
}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec
pragma solidity >=0.8.4;
library AztecTypes {
enum AztecAssetType {
NOT_USED,
ETH,
ERC20,
VIRTUAL
}
struct AztecAsset {
uint256 id;
address erc20Address;
AztecAssetType assetType;
}
}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec
pragma solidity >=0.8.4;
import {AztecTypes} from "rollup-encoder/libraries/AztecTypes.sol";
interface IDefiBridge {
/**
* @notice A function which converts input assets to output assets.
* @param _inputAssetA A struct detailing the first input asset
* @param _inputAssetB A struct detailing the second input asset
* @param _outputAssetA A struct detailing the first output asset
* @param _outputAssetB A struct detailing the second output asset
* @param _totalInputValue An amount of input assets transferred to the bridge (Note: "total" is in the name
* because the value can represent summed/aggregated token amounts of users actions on L2)
* @param _interactionNonce A globally unique identifier of this interaction/`convert(...)` call.
* @param _auxData Bridge specific data to be passed into the bridge contract (e.g. slippage, nftID etc.)
* @return outputValueA An amount of `_outputAssetA` returned from this interaction.
* @return outputValueB An amount of `_outputAssetB` returned from this interaction.
* @return isAsync A flag indicating if the interaction is async.
* @dev This function is called from the RollupProcessor contract via the DefiBridgeProxy. Before this function is
* called _RollupProcessor_ contract will have sent you all the assets defined by the input params. This
* function is expected to convert input assets to output assets (e.g. on Uniswap) and return the amounts
* of output assets to be received by the _RollupProcessor_. If output assets are ERC20 tokens the bridge has
* to _RollupProcessor_ as a spender before the interaction is finished. If some of the output assets is ETH
* it has to be sent to _RollupProcessor_ via the `receiveEthFromBridge(uint256 _interactionNonce)` method
* inside before the `convert(...)` function call finishes.
* @dev If there are two input assets, equal amounts of both assets will be transferred to the bridge before this
* method is called.
* @dev **BOTH** output assets could be virtual but since their `assetId` is currently assigned as
* `_interactionNonce` it would simply mean that more of the same virtual asset is minted.
* @dev If this interaction is async the function has to return `(0,0 true)`. Async interaction will be finalised at
* a later time and its output assets will be returned in a `IDefiBridge.finalise(...)` call.
*
*/
function convert(
AztecTypes.AztecAsset calldata _inputAssetA,
AztecTypes.AztecAsset calldata _inputAssetB,
AztecTypes.AztecAsset calldata _outputAssetA,
AztecTypes.AztecAsset calldata _outputAssetB,
uint256 _totalInputValue,
uint256 _interactionNonce,
uint64 _auxData,
address _rollupBeneficiary
) external payable returns (uint256 outputValueA, uint256 outputValueB, bool isAsync);
/**
* @notice A function that finalises asynchronous interaction.
* @param _inputAssetA A struct detailing the first input asset
* @param _inputAssetB A struct detailing the second input asset
* @param _outputAssetA A struct detailing the first output asset
* @param _outputAssetB A struct detailing the second output asset
* @param _interactionNonce A globally unique identifier of this interaction/`convert(...)` call.
* @param _auxData Bridge specific data to be passed into the bridge contract (e.g. slippage, nftID etc.)
* @return outputValueA An amount of `_outputAssetA` returned from this interaction.
* @return outputValueB An amount of `_outputAssetB` returned from this interaction.
* @dev This function should use the `BridgeBase.onlyRollup()` modifier to ensure it can only be called from
* the `RollupProcessor.processAsyncDefiInteraction(uint256 _interactionNonce)` method.
*
*/
function finalise(
AztecTypes.AztecAsset calldata _inputAssetA,
AztecTypes.AztecAsset calldata _inputAssetB,
AztecTypes.AztecAsset calldata _outputAssetA,
AztecTypes.AztecAsset calldata _outputAssetB,
uint256 _interactionNonce,
uint64 _auxData
) external payable returns (uint256 outputValueA, uint256 outputValueB, bool interactionComplete);
}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec
pragma solidity >=0.8.4;
// @dev documentation of this interface is in its implementation (Subsidy contract)
interface ISubsidy {
/**
* @notice Container for Subsidy related information
* @member available Amount of ETH remaining to be paid out
* @member gasUsage Amount of gas the interaction consumes (used to define max possible payout)
* @member minGasPerMinute Minimum amount of gas per minute the subsidizer has to subsidize
* @member gasPerMinute Amount of gas per minute the subsidizer is willing to subsidize
* @member lastUpdated Last time subsidy was paid out or funded (if not subsidy was yet claimed after funding)
*/
struct Subsidy {
uint128 available;
uint32 gasUsage;
uint32 minGasPerMinute;
uint32 gasPerMinute;
uint32 lastUpdated;
}
function setGasUsageAndMinGasPerMinute(uint256 _criteria, uint32 _gasUsage, uint32 _minGasPerMinute) external;
function setGasUsageAndMinGasPerMinute(
uint256[] calldata _criteria,
uint32[] calldata _gasUsage,
uint32[] calldata _minGasPerMinute
) external;
function registerBeneficiary(address _beneficiary) external;
function subsidize(address _bridge, uint256 _criteria, uint32 _gasPerMinute) external payable;
function topUp(address _bridge, uint256 _criteria) external payable;
function claimSubsidy(uint256 _criteria, address _beneficiary) external returns (uint256);
function withdraw(address _beneficiary) external returns (uint256);
// solhint-disable-next-line
function MIN_SUBSIDY_VALUE() external view returns (uint256);
function claimableAmount(address _beneficiary) external view returns (uint256);
function isRegistered(address _beneficiary) external view returns (bool);
function getSubsidy(address _bridge, uint256 _criteria) external view returns (Subsidy memory);
function getAccumulatedSubsidyAmount(address _bridge, uint256 _criteria) external view returns (uint256);
}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec.
pragma solidity >=0.8.4;
import {IDefiBridge} from "../../aztec/interfaces/IDefiBridge.sol";
import {ISubsidy} from "../../aztec/interfaces/ISubsidy.sol";
import {AztecTypes} from "rollup-encoder/libraries/AztecTypes.sol";
import {ErrorLib} from "./ErrorLib.sol";
/**
* @title BridgeBase
* @notice A base that bridges can be built upon which imports a limited set of features
* @dev Reverts `convert` with missing implementation, and `finalise` with async disabled
* @author Lasse Herskind
*/
abstract contract BridgeBase is IDefiBridge {
error MissingImplementation();
ISubsidy public constant SUBSIDY = ISubsidy(0xABc30E831B5Cc173A9Ed5941714A7845c909e7fA);
address public immutable ROLLUP_PROCESSOR;
constructor(address _rollupProcessor) {
ROLLUP_PROCESSOR = _rollupProcessor;
}
modifier onlyRollup() {
if (msg.sender != ROLLUP_PROCESSOR) {
revert ErrorLib.InvalidCaller();
}
_;
}
function convert(
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
uint256,
uint256,
uint64,
address
) external payable virtual override(IDefiBridge) returns (uint256, uint256, bool) {
revert MissingImplementation();
}
function finalise(
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
uint256,
uint64
) external payable virtual override(IDefiBridge) returns (uint256, uint256, bool) {
revert ErrorLib.AsyncDisabled();
}
/**
* @notice Computes the criteria that is passed on to the subsidy contract when claiming
* @dev Should be overridden by bridge implementation if intended to limit subsidy.
* @return The criteria to be passed along
*/
function computeCriteria(
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata,
uint64
) public view virtual returns (uint256) {
return 0;
}
}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec.
pragma solidity >=0.8.4;
library ErrorLib {
error InvalidCaller();
error InvalidInput();
error InvalidInputA();
error InvalidInputB();
error InvalidOutputA();
error InvalidOutputB();
error InvalidInputAmount();
error InvalidAuxData();
error ApproveFailed(address token);
error TransferFailed(address token);
error InvalidNonce();
error AsyncDisabled();
}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec.
pragma solidity >=0.8.4;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IRollupProcessor} from "rollup-encoder/interfaces/IRollupProcessor.sol";
import {AztecTypes} from "rollup-encoder/libraries/AztecTypes.sol";
import {BridgeBase} from "../base/BridgeBase.sol";
import {ErrorLib} from "../base/ErrorLib.sol";
import {IWETH} from "../../interfaces/IWETH.sol";
import {ISwapRouter} from "../../interfaces/uniswapv3/ISwapRouter.sol";
import {IVault, IAsset, PoolSpecialization} from "../../interfaces/element/IVault.sol";
interface IMigrator {
function migrate(uint256 _amount, bytes32 _acceptanceToken) external returns (uint256, uint256, uint256);
function ERC4626Token() external view returns (address);
}
contract EulerRedemptionBridge is BridgeBase {
using SafeERC20 for IERC20;
using SafeERC20 for IERC4626;
error SlippageExceeded();
IWETH public constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
IERC20 public constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
IERC20 public constant WSTETH = IERC20(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);
IERC4626 public constant WEWETH = IERC4626(0x3c66B18F67CA6C1A71F829E2F6a0c987f97462d0);
IERC4626 public constant WEDAI = IERC4626(0x4169Df1B7820702f566cc10938DA51F6F597d264);
IERC4626 public constant WEWSTETH = IERC4626(0x60897720AA966452e8706e74296B018990aEc527);
bytes32 public constant TERMS_AND_CONDITIONS_HASH =
0x427a506ff6e15bd1b7e4e93da52c8ec95f6af1279618a2f076946e83d8294996;
ISwapRouter public constant ROUTER = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
IVault public constant BALANCER = IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
bytes32 public constant BALANCER_WSTETH_POOLID = 0x32296969ef14eb0c6d29669c550d4a0449130230000200000000000000000080;
IMigrator public immutable WETH_MIGRATOR;
IMigrator public immutable DAI_MIGRATOR;
IMigrator public immutable WSTETH_MIGRATOR;
constructor(address _rollupProcessor, address _wethMigrator, address _daiMigrator, address _wstethMigrator)
BridgeBase(_rollupProcessor)
{
WETH_MIGRATOR = IMigrator(_wethMigrator);
DAI_MIGRATOR = IMigrator(_daiMigrator);
WSTETH_MIGRATOR = IMigrator(_wstethMigrator);
IERC20(WETH_MIGRATOR.ERC4626Token()).approve(address(WETH_MIGRATOR), type(uint256).max);
IERC20(DAI_MIGRATOR.ERC4626Token()).approve(address(DAI_MIGRATOR), type(uint256).max);
IERC20(WSTETH_MIGRATOR.ERC4626Token()).approve(address(WSTETH_MIGRATOR), type(uint256).max);
WETH.approve(address(BALANCER), type(uint256).max);
WETH.approve(address(ROUTER), type(uint256).max);
DAI.approve(address(ROUTER), type(uint256).max);
USDC.approve(address(ROUTER), type(uint256).max);
DAI.approve(address(ROLLUP_PROCESSOR), type(uint256).max);
WSTETH.approve(address(ROLLUP_PROCESSOR), type(uint256).max);
}
receive() external payable {}
/**
* @notice Redeems shares of Euler ERC4626 vaults for underlying assets
* following the redemption scheme. Will take the assets received and swap it into
* the expected underlying.
* @param _inputAssetA - The input asset to redeem
* @param _outputAssetA - The output asset to receive
* @param _totalInputValue - The total amount of input asset to redeem
* @param _interactionNonce - The nonce of the interaction
* @param _auxData - The aux data of the interaction (minAmountPerFullShare)
* @return outputValueA - The amount of output asset received
*/
function convert(
AztecTypes.AztecAsset calldata _inputAssetA,
AztecTypes.AztecAsset calldata,
AztecTypes.AztecAsset calldata _outputAssetA,
AztecTypes.AztecAsset calldata,
uint256 _totalInputValue,
uint256 _interactionNonce,
uint64 _auxData,
address
) external payable override(BridgeBase) onlyRollup returns (uint256 outputValueA, uint256, bool) {
if (_inputAssetA.erc20Address == address(WEWETH)) {
return _exitWeweth(_totalInputValue, _outputAssetA.erc20Address, _interactionNonce, _auxData);
} else if (_inputAssetA.erc20Address == address(WEDAI)) {
return _exitDai(_totalInputValue, _outputAssetA.erc20Address, _auxData);
} else if (_inputAssetA.erc20Address == address(WEWSTETH)) {
return _exitWsteth(_totalInputValue, _outputAssetA.erc20Address, _auxData);
} else {
revert ErrorLib.InvalidInputA();
}
}
/**
* @notice Redeems shares of WEWETH vault for weth, dai, usdc
* swaps assets to Eth
* @param _totalInputValue - The total amount of input asset to redeem
* @param _outputAssetA - The output asset to receive
* @param _interactionNonce - The nonce of the interaction
* @param _auxData - The aux data of the interaction (minAmountPerFullShare)
* @return outputValueA - The amount of output asset received
*/
function _exitWeweth(uint256 _totalInputValue, address _outputAssetA, uint256 _interactionNonce, uint64 _auxData)
internal
returns (uint256 outputValueA, uint256, bool)
{
if (_outputAssetA != address(0)) {
revert ErrorLib.InvalidOutputA();
}
// Migrate the asset.
(uint256 wethAmount, uint256 daiAmount, uint256 usdcAmount) =
WETH_MIGRATOR.migrate(_totalInputValue, _acceptanceToken());
// Swap dai for usdc on uniswap
{
if (daiAmount > 0) {
bytes memory path = abi.encodePacked(address(DAI), uint24(100), address(USDC));
usdcAmount += ROUTER.exactInput(
ISwapRouter.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: daiAmount,
amountOutMinimum: 0
})
);
}
}
// Swap usdc to weth
{
if (usdcAmount > 0) {
bytes memory path = abi.encodePacked(address(USDC), uint24(500), address(WETH));
wethAmount += ROUTER.exactInput(
ISwapRouter.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: usdcAmount,
amountOutMinimum: 0
})
);
}
}
// @todo slippage aux could be 1e16 precision if there are very high interest amounts.
uint256 minExpected = _totalInputValue * _auxData / 1e18;
if (wethAmount < minExpected) {
revert SlippageExceeded();
}
IWETH(WETH).withdraw(wethAmount);
IRollupProcessor(ROLLUP_PROCESSOR).receiveEthFromBridge{value: wethAmount}(_interactionNonce);
return (wethAmount, 0, false);
}
/**
* @notice Redeems shares of WEDAI vault for weth, dai, usdc
* swaps assets to Dai
* @param _totalInputValue - The total amount of input asset to redeem
* @param _outputAssetA - The output asset to receive
* @param _auxData - The aux data of the interaction (minAmountPerFullShare)
* @return outputValueA - The amount of output asset received
*/
function _exitDai(uint256 _totalInputValue, address _outputAssetA, uint64 _auxData)
internal
returns (uint256 outputValueA, uint256, bool)
{
if (_outputAssetA != address(DAI)) {
revert ErrorLib.InvalidOutputA();
}
// Migrate the asset.
(uint256 wethAmount, uint256 daiAmount, uint256 usdcAmount) =
DAI_MIGRATOR.migrate(_totalInputValue, _acceptanceToken());
// Swap weth to usdc
{
if (wethAmount > 0) {
bytes memory path = abi.encodePacked(address(WETH), uint24(500), address(USDC));
usdcAmount += ROUTER.exactInput(
ISwapRouter.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: wethAmount,
amountOutMinimum: 0
})
);
}
}
// Swap usdc for dai on uniswap
{
if (usdcAmount > 0) {
bytes memory path = abi.encodePacked(address(USDC), uint24(100), address(DAI));
daiAmount += ROUTER.exactInput(
ISwapRouter.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: usdcAmount,
amountOutMinimum: 0
})
);
}
}
// @todo slippage aux could be 1e16 precision if there are very high interest amounts.
uint256 minExpected = _totalInputValue * _auxData / 1e18;
if (daiAmount < minExpected) {
revert SlippageExceeded();
}
return (daiAmount, 0, false);
}
/**
* @notice Redeems shares of WEWESTETH vault for weth, dai, usdc
* swaps assets to Wsteth
* @param _totalInputValue - The total amount of input asset to redeem
* @param _outputAssetA - The output asset to receive
* @param _auxData - The aux data of the interaction (minAmountPerFullShare)
* @return outputValueA - The amount of output asset received
*/
function _exitWsteth(uint256 _totalInputValue, address _outputAssetA, uint64 _auxData)
internal
returns (uint256 outputValueA, uint256, bool)
{
if (_outputAssetA != address(WSTETH)) {
revert ErrorLib.InvalidOutputA();
}
// Migrate the asset.
(uint256 wethAmount, uint256 daiAmount, uint256 usdcAmount) =
WSTETH_MIGRATOR.migrate(_totalInputValue, _acceptanceToken());
// Swap dai for usdc on uniswap
{
if (daiAmount > 0) {
bytes memory path = abi.encodePacked(address(DAI), uint24(100), address(USDC));
usdcAmount += ROUTER.exactInput(
ISwapRouter.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: daiAmount,
amountOutMinimum: 0
})
);
}
}
// Swap usdc to weth
{
if (usdcAmount > 0) {
bytes memory path = abi.encodePacked(address(USDC), uint24(500), address(WETH));
wethAmount += ROUTER.exactInput(
ISwapRouter.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: usdcAmount,
amountOutMinimum: 0
})
);
}
}
// Swap weth to wsteth
uint256 wstethBal;
{
if (wethAmount > 0) {
IVault.SingleSwap memory singleSwap = IVault.SingleSwap({
poolId: BALANCER_WSTETH_POOLID,
kind: IVault.SwapKind.GIVEN_IN,
assetIn: IAsset(address(WETH)),
assetOut: IAsset(address(WSTETH)),
amount: wethAmount,
userData: "0x00"
});
IVault.FundManagement memory fundManagement = IVault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
});
wstethBal = BALANCER.swap(singleSwap, fundManagement, 0, block.timestamp);
}
}
// @todo slippage aux could be 1e16 precision if there are very high interest amounts.
uint256 minExpected = _totalInputValue * _auxData / 1e18;
if (wstethBal < minExpected) {
revert SlippageExceeded();
}
return (wstethBal, 0, false);
}
/**
* @notice Computes the acceptance token for the migration.
* @return The acceptance token.
*/
function _acceptanceToken() internal view returns (bytes32) {
return keccak256(abi.encodePacked(address(this), TERMS_AND_CONDITIONS_HASH));
}
}
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec.
pragma solidity >=0.8.4;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IAsset {
// solhint-disable-previous-line no-empty-blocks
}
enum PoolSpecialization {
GENERAL,
MINIMAL_SWAP_INFO,
TWO_TOKEN
}
interface IVault {
enum SwapKind {
GIVEN_IN,
GIVEN_OUT
}
/**
* @dev Performs a swap with a single Pool.
*
* If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens
* taken from the Pool, which must be greater than or equal to `limit`.
*
* If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens
* sent to the Pool, which must be less than or equal to `limit`.
*
* Internal Balance usage and the recipient are determined by the `funds` struct.
*
* Emits a `Swap` event.
*/
function swap(SingleSwap memory singleSwap, FundManagement memory funds, uint256 limit, uint256 deadline)
external
payable
returns (uint256);
/**
* @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on
* the `kind` value.
*
* `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address).
* Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault.
*
* The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
* used to extend swap behavior.
*/
struct SingleSwap {
bytes32 poolId;
SwapKind kind;
IAsset assetIn;
IAsset assetOut;
uint256 amount;
bytes userData;
}
/**
* @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the
* `recipient` account.
*
* If the caller is not `sender`, it must be an authorized relayer for them.
*
* If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20
* transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender`
* must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of
* `joinPool`.
*
* If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of
* transferred. This matches the behavior of `exitPool`.
*
* Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a
* revert.
*/
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
// will revert if poolId is not a registered pool
function getPool(bytes32 poolId) external view returns (address, PoolSpecialization);
/**
* @dev Simulates a call to `batchSwap`, returning an array of Vault asset deltas. Calls to `swap` cannot be
* simulated directly, but an equivalent `batchSwap` call can and will yield the exact same result.
*
* Each element in the array corresponds to the asset at the same index, and indicates the number of tokens (or ETH)
* the Vault would take from the sender (if positive) or send to the recipient (if negative). The arguments it
* receives are the same that an equivalent `batchSwap` call would receive.
*
* Unlike `batchSwap`, this function performs no checks on the sender or recipient field in the `funds` struct.
* This makes it suitable to be called by off-chain applications via eth_call without needing to hold tokens,
* approve them for the Vault, or even know a user's address.
*
* Note that this function is not 'view' (due to implementation details): the client code must explicitly execute
* eth_call instead of eth_sendTransaction.
*/
struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
function queryBatchSwap(
SwapKind kind,
BatchSwapStep[] memory swaps,
IAsset[] memory assets,
FundManagement memory funds
) external view returns (int256[] memory assetDeltas);
/**
* @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of
* the tokens' `balances` changed.
*
* The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all
* Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.
*
* If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same
* order as passed to `registerTokens`.
*
* Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are
* the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`
* instead.
*/
function getPoolTokens(bytes32 poolId)
external
view
returns (IERC20[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}