// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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.8.0/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: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)
pragma solidity ^0.8.0;
import "../IERC1155Receiver.sol";
import "../../../utils/introspection/ERC165.sol";
/**
* @dev _Available since v3.1._
*/
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @title Library for errors
/// @author Timeswap Labs
/// @dev Common error messages
library Error {
/// @dev Reverts when input is zero.
error ZeroInput();
/// @dev Reverts when output is zero.
error ZeroOutput();
/// @dev Reverts when a value cannot be zero.
error CannotBeZero();
/// @dev Reverts when a pool already have liquidity.
/// @param liquidity The liquidity amount that already existed in the pool.
error AlreadyHaveLiquidity(uint160 liquidity);
/// @dev Reverts when a pool requires liquidity.
error RequireLiquidity();
/// @dev Reverts when a given address is the zero address.
error ZeroAddress();
/// @dev Reverts when the maturity given is not withing uint96.
/// @param maturity The maturity being inquired.
error IncorrectMaturity(uint256 maturity);
/// @dev Reverts when an option of given strike and maturity is still inactive.
/// @param strike The chosen strike.
/// @param maturity The chosen maturity.
error InactiveOption(uint256 strike, uint256 maturity);
/// @dev Reverts when a pool of given strike and maturity is still inactive.
/// @param strike The chosen strike.
/// @param maturity The chosen maturity.
error InactivePool(uint256 strike, uint256 maturity);
/// @dev Reverts when a liquidity token is inactive.
error InactiveLiquidityTokenChoice();
/// @dev Reverts when the square root interest rate is zero.
/// @param strike The chosen strike.
/// @param maturity The chosen maturity.
error ZeroSqrtInterestRate(uint256 strike, uint256 maturity);
/// @dev Reverts when the maturity is already matured.
/// @param maturity The maturity.
/// @param blockTimestamp The current block timestamp.
error AlreadyMatured(uint256 maturity, uint96 blockTimestamp);
/// @dev Reverts when the maturity is still active.
/// @param maturity The maturity.
/// @param blockTimestamp The current block timestamp.
error StillActive(uint256 maturity, uint96 blockTimestamp);
/// @dev Token amount not received.
/// @param minuend The amount being subtracted.
/// @param subtrahend The amount subtracting.
error NotEnoughReceived(uint256 minuend, uint256 subtrahend);
/// @dev The deadline of a transaction has been reached.
/// @param deadline The deadline set.
error DeadlineReached(uint256 deadline);
/// @dev Reverts when input is zero.
function zeroInput() internal pure {
revert ZeroInput();
}
/// @dev Reverts when output is zero.
function zeroOutput() internal pure {
revert ZeroOutput();
}
/// @dev Reverts when a value cannot be zero.
function cannotBeZero() internal pure {
revert CannotBeZero();
}
/// @dev Reverts when a pool already have liquidity.
/// @param liquidity The liquidity amount that already existed in the pool.
function alreadyHaveLiquidity(uint160 liquidity) internal pure {
revert AlreadyHaveLiquidity(liquidity);
}
/// @dev Reverts when a pool requires liquidity.
function requireLiquidity() internal pure {
revert RequireLiquidity();
}
/// @dev Reverts when a given address is the zero address.
function zeroAddress() internal pure {
revert ZeroAddress();
}
/// @dev Reverts when the maturity given is not withing uint96.
/// @param maturity The maturity being inquired.
function incorrectMaturity(uint256 maturity) internal pure {
revert IncorrectMaturity(maturity);
}
/// @dev Reverts when the maturity is already matured.
/// @param maturity The maturity.
/// @param blockTimestamp The current block timestamp.
function alreadyMatured(uint256 maturity, uint96 blockTimestamp) internal pure {
revert AlreadyMatured(maturity, blockTimestamp);
}
/// @dev Reverts when the maturity is still active.
/// @param maturity The maturity.
/// @param blockTimestamp The current block timestamp.
function stillActive(uint256 maturity, uint96 blockTimestamp) internal pure {
revert StillActive(maturity, blockTimestamp);
}
/// @dev The deadline of a transaction has been reached.
/// @param deadline The deadline set.
function deadlineReached(uint256 deadline) internal pure {
revert DeadlineReached(deadline);
}
/// @dev Reverts when an option of given strike and maturity is still inactive.
/// @param strike The chosen strike.
function inactiveOptionChoice(uint256 strike, uint256 maturity) internal pure {
revert InactiveOption(strike, maturity);
}
/// @dev Reverts when a pool of given strike and maturity is still inactive.
/// @param strike The chosen strike.
/// @param maturity The chosen maturity.
function inactivePoolChoice(uint256 strike, uint256 maturity) internal pure {
revert InactivePool(strike, maturity);
}
/// @dev Reverts when the square root interest rate is zero.
/// @param strike The chosen strike.
/// @param maturity The chosen maturity.
function zeroSqrtInterestRate(uint256 strike, uint256 maturity) internal pure {
revert ZeroSqrtInterestRate(strike, maturity);
}
/// @dev Reverts when a liquidity token is inactive.
function inactiveLiquidityTokenChoice() internal pure {
revert InactiveLiquidityTokenChoice();
}
/// @dev Reverts when token amount not received.
/// @param balance The balance amount being subtracted.
/// @param balanceTarget The amount target.
function checkEnough(uint256 balance, uint256 balanceTarget) internal pure {
if (balance < balanceTarget) revert NotEnoughReceived(balance, balanceTarget);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
// SPDX-License-Identifier: BUSL-1.1
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
/// @title ERC-1155 Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
interface IERC1155Enumerable is IERC1155 {
/// @dev Returns the total amount of ids with positive supply stored by the contract.
function totalIds() external view returns (uint256);
/// @dev Returns the total supply of a token given its id.
/// @param id The index of the queried token.
function totalSupply(uint256 id) external view returns (uint256);
/// @dev Returns a token ID owned by `owner` at a given `index` of its token list.
/// Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/// @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
/// Use along with {totalSupply} to enumerate all tokens.
function tokenByIndex(uint256 index) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev _Available since v3.1._
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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 (last updated v4.9.4) (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.
*
* ==== 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);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @title Multicall interface
/// @notice Enables calling multiple methods in a single call to the contract
interface IMulticall {
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
error MulticallFailed(string revertString);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @title NativeImmutableState interface
interface INativeImmutableState {
/// @return Returns the address of Wrapped Native token
function wrappedNativeToken() external view returns (address);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @title NativePayments interface
/// @notice Functions to ease payments of native tokens
interface INativePayments {
/// @notice Refunds any Native Token balance held by this contract to the `msg.sender`
function refundNatives() external payable;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @title NativeWithdraws interface
interface INativeWithdraws {
/// @notice Unwraps the contract's Wrapped Native token balance and sends it to recipient as Native token.
/// @dev The amountMinimum parameter prevents malicious contracts from stealing Wrapped Native from users.
/// @param amountMinimum The minimum amount of Wrapped Native to unwrap
/// @param recipient The address receiving Native token
function unwrapWrappedNatives(uint256 amountMinimum, address recipient) external payable;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {IERC1155Enumerable} from "./IERC1155Enumerable.sol";
import {TimeswapV2LiquidityTokenPosition} from "../structs/Position.sol";
import {TimeswapV2LiquidityTokenMintParam, TimeswapV2LiquidityTokenBurnParam, TimeswapV2LiquidityTokenCollectParam} from "../structs/Param.sol";
/// @title An interface for TS-V2 liquidity token system
interface ITimeswapV2LiquidityToken is IERC1155Enumerable {
error NotApprovedToTransferFees();
/// @dev Returns the option factory address.
/// @return optionFactory The option factory address.
function optionFactory() external view returns (address);
/// @dev Returns the pool factory address.
/// @return poolFactory The pool factory address.
function poolFactory() external view returns (address);
/// @dev Returns the position Balance of the owner
/// @param owner The owner of the token
/// @param position The liquidity position
function positionOf(
address owner,
TimeswapV2LiquidityTokenPosition calldata position
) external view returns (uint256 amount);
/// @dev Returns the fee and short returned growth of the pool
/// @param position The liquidity position
/// @return long0FeeGrowth The long0 fee growth
/// @return long1FeeGrowth The long1 fee growth
/// @return shortFeeGrowth The short fee growth
/// @return shortReturnedGrowth The short returned growth
function feesEarnedAndShortReturnedGrowth(
TimeswapV2LiquidityTokenPosition calldata position
)
external
view
returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth, uint256 shortReturnedGrowth);
/// @dev Returns the fee and short returned growth of the pool
/// @param position The liquidity position
/// @param durationForward The time duration forward
/// @return long0FeeGrowth The long0 fee growth
/// @return long1FeeGrowth The long1 fee growth
/// @return shortFeeGrowth The short fee growth
/// @return shortReturnedGrowth The short returned growth
function feesEarnedAndShortReturnedGrowth(
TimeswapV2LiquidityTokenPosition calldata position,
uint96 durationForward
)
external
view
returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth, uint256 shortReturnedGrowth);
/// @param owner The address to query the fees earned and short returned of.
/// @param position The liquidity token position.
/// @return long0Fees The amount of long0 fees owned by the given address.
/// @return long1Fees The amount of long1 fees owned by the given address.
/// @return shortFees The amount of short fees owned by the given address.
/// @return shortReturned The amount of short returned owned by the given address.
function feesEarnedAndShortReturnedOf(
address owner,
TimeswapV2LiquidityTokenPosition calldata position
) external view returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned);
/// @param owner The address to query the fees earned and short returned of.
/// @param position The liquidity token position.
/// @param durationForward The time duration forward
/// @return long0Fees The amount of long0 fees owned by the given address.
/// @return long1Fees The amount of long1 fees owned by the given address.
/// @return shortFees The amount of short fees owned by the given address.
/// @return shortReturned The amount of short returned owned by the given address.
function feesEarnedAndShortReturnedOf(
address owner,
TimeswapV2LiquidityTokenPosition calldata position,
uint96 durationForward
) external view returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned);
/// @dev Transfers position token TimeswapV2Token from `from` to `to`
/// @param from The address to transfer position token from
/// @param to The address to transfer position token to
/// @param position The TimeswapV2Token Position to transfer
/// @param liquidityAmount The amount of TimeswapV2Token Position to transfer
/// @param erc1155Data Aribtrary custom data for erc1155 transfer
function transferTokenPositionFrom(
address from,
address to,
TimeswapV2LiquidityTokenPosition calldata position,
uint160 liquidityAmount,
bytes calldata erc1155Data
) external;
/// @dev mints TimeswapV2LiquidityToken as per the liqudityAmount
/// @param param The TimeswapV2LiquidityTokenMintParam
/// @return data Arbitrary data
function mint(TimeswapV2LiquidityTokenMintParam calldata param) external returns (bytes memory data);
/// @dev burns TimeswapV2LiquidityToken as per the liqudityAmount
/// @param param The TimeswapV2LiquidityTokenBurnParam
/// @return data Arbitrary data
function burn(TimeswapV2LiquidityTokenBurnParam calldata param) external returns (bytes memory data);
/// @dev collects fees as per the fees desired
/// @param param The TimeswapV2LiquidityTokenBurnParam
/// @return long0Fees Fees for long0
/// @return long1Fees Fees for long1
/// @return shortFees Fees for short
/// @return shortReturned Short Returned
function collect(
TimeswapV2LiquidityTokenCollectParam calldata param
)
external
returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned, bytes memory data);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {TimeswapV2OptionPosition} from "../enums/Position.sol";
import {TimeswapV2OptionMintParam, TimeswapV2OptionBurnParam, TimeswapV2OptionSwapParam, TimeswapV2OptionCollectParam} from "../structs/Param.sol";
import {StrikeAndMaturity} from "../structs/StrikeAndMaturity.sol";
/// @title An interface for a contract that deploys Timeswap V2 Option pair contracts
/// @notice A Timeswap V2 Option pair facilitates option mechanics between any two assets that strictly conform
/// to the ERC20 specification.
interface ITimeswapV2Option {
/* ===== EVENT ===== */
/// @dev Emits when a position is transferred.
/// @param strike The strike ratio of token1 per token0 of the position.
/// @param maturity The maturity of the position.
/// @param from The address of the caller of the transferPosition function.
/// @param to The address of the recipient of the position.
/// @param position The type of position transferred. More information in the Position module.
/// @param amount The amount of balance transferred.
event TransferPosition(
uint256 indexed strike,
uint256 indexed maturity,
address from,
address to,
TimeswapV2OptionPosition position,
uint256 amount
);
/// @dev Emits when a mint transaction is called.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param caller The address of the caller of the mint function.
/// @param long0To The address of the recipient of long token0 position.
/// @param long1To The address of the recipient of long token1 position.
/// @param shortTo The address of the recipient of short position.
/// @param token0AndLong0Amount The amount of token0 deposited and long0 minted.
/// @param token1AndLong1Amount The amount of token1 deposited and long1 minted.
/// @param shortAmount The amount of short minted.
event Mint(
uint256 indexed strike,
uint256 indexed maturity,
address indexed caller,
address long0To,
address long1To,
address shortTo,
uint256 token0AndLong0Amount,
uint256 token1AndLong1Amount,
uint256 shortAmount
);
/// @dev Emits when a burn transaction is called.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param caller The address of the caller of the mint function.
/// @param token0To The address of the recipient of token0.
/// @param token1To The address of the recipient of token1.
/// @param token0AndLong0Amount The amount of token0 withdrawn and long0 burnt.
/// @param token1AndLong1Amount The amount of token1 withdrawn and long1 burnt.
/// @param shortAmount The amount of short burnt.
event Burn(
uint256 indexed strike,
uint256 indexed maturity,
address indexed caller,
address token0To,
address token1To,
uint256 token0AndLong0Amount,
uint256 token1AndLong1Amount,
uint256 shortAmount
);
/// @dev Emits when a swap transaction is called.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param caller The address of the caller of the mint function.
/// @param tokenTo The address of the recipient of token0 or token1.
/// @param longTo The address of the recipient of long token0 or long token1.
/// @param isLong0toLong1 The direction of the swap. More information in the Transaction module.
/// @param token0AndLong0Amount If the direction is from long0 to long1, the amount of token0 withdrawn and long0 burnt.
/// If the direction is from long1 to long0, the amount of token0 deposited and long0 minted.
/// @param token1AndLong1Amount If the direction is from long0 to long1, the amount of token1 deposited and long1 minted.
/// If the direction is from long1 to long0, the amount of token1 withdrawn and long1 burnt.
event Swap(
uint256 indexed strike,
uint256 indexed maturity,
address indexed caller,
address tokenTo,
address longTo,
bool isLong0toLong1,
uint256 token0AndLong0Amount,
uint256 token1AndLong1Amount
);
/// @dev Emits when a collect transaction is called.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param caller The address of the caller of the mint function.
/// @param token0To The address of the recipient of token0.
/// @param token1To The address of the recipient of token1.
/// @param long0AndToken0Amount The amount of token0 withdrawn.
/// @param long1AndToken1Amount The amount of token1 withdrawn.
/// @param shortAmount The amount of short burnt.
event Collect(
uint256 indexed strike,
uint256 indexed maturity,
address indexed caller,
address token0To,
address token1To,
uint256 long0AndToken0Amount,
uint256 long1AndToken1Amount,
uint256 shortAmount
);
/* ===== VIEW ===== */
/// @dev Returns the factory address that deployed this contract.
function optionFactory() external view returns (address);
/// @dev Returns the first ERC20 token address of the pair.
function token0() external view returns (address);
/// @dev Returns the second ERC20 token address of the pair.
function token1() external view returns (address);
/// @dev Get the strike and maturity of the option in the option enumeration list.
/// @param id The chosen index.
function getByIndex(uint256 id) external view returns (StrikeAndMaturity memory);
/// @dev Number of options being interacted.
function numberOfOptions() external view returns (uint256);
/// @dev Returns the total position of the option.
/// @param strike The strike ratio of token1 per token0 of the position.
/// @param maturity The maturity of the position.
/// @param position The type of position inquired. More information in the Position module.
/// @return balance The total position.
function totalPosition(
uint256 strike,
uint256 maturity,
TimeswapV2OptionPosition position
) external view returns (uint256 balance);
/// @dev Returns the position of an owner of the option.
/// @param strike The strike ratio of token1 per token0 of the position.
/// @param maturity The maturity of the position.
/// @param owner The address of the owner of the position.
/// @param position The type of position inquired. More information in the Position module.
/// @return balance The user position.
function positionOf(
uint256 strike,
uint256 maturity,
address owner,
TimeswapV2OptionPosition position
) external view returns (uint256 balance);
/* ===== UPDATE ===== */
/// @dev Transfer position to another address.
/// @param strike The strike ratio of token1 per token0 of the position.
/// @param maturity The maturity of the position.
/// @param to The address of the recipient of the position.
/// @param position The type of position transferred. More information in the Position module.
/// @param amount The amount of balance transferred.
function transferPosition(
uint256 strike,
uint256 maturity,
address to,
TimeswapV2OptionPosition position,
uint256 amount
) external;
/// @dev Mint position.
/// Mint long token0 position when token0 is deposited.
/// Mint long token1 position when token1 is deposited.
/// @dev Can only be called before the maturity of the pool.
/// @param param The parameters for the mint function.
/// @return token0AndLong0Amount The amount of token0 deposited and long0 minted.
/// @return token1AndLong1Amount The amount of token1 deposited and long1 minted.
/// @return shortAmount The amount of short minted.
/// @return data The additional data return.
function mint(
TimeswapV2OptionMintParam calldata param
)
external
returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount, bytes memory data);
/// @dev Burn short position.
/// Withdraw token0, when long token0 is burnt.
/// Withdraw token1, when long token1 is burnt.
/// @dev Can only be called before the maturity of the pool.
/// @param param The parameters for the burn function.
/// @return token0AndLong0Amount The amount of token0 withdrawn and long0 burnt.
/// @return token1AndLong1Amount The amount of token1 withdrawn and long1 burnt.
/// @return shortAmount The amount of short burnt.
function burn(
TimeswapV2OptionBurnParam calldata param
)
external
returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount, bytes memory data);
/// @dev If the direction is from long token0 to long token1, burn long token0 and mint equivalent long token1,
/// also deposit token1 and withdraw token0.
/// If the direction is from long token1 to long token0, burn long token1 and mint equivalent long token0,
/// also deposit token0 and withdraw token1.
/// @dev Can only be called before the maturity of the pool.
/// @param param The parameters for the swap function.
/// @return token0AndLong0Amount If direction is Long0ToLong1, the amount of token0 withdrawn and long0 burnt.
/// If direction is Long1ToLong0, the amount of token0 deposited and long0 minted.
/// @return token1AndLong1Amount If direction is Long0ToLong1, the amount of token1 deposited and long1 minted.
/// If direction is Long1ToLong0, the amount of token1 withdrawn and long1 burnt.
/// @return data The additional data return.
function swap(
TimeswapV2OptionSwapParam calldata param
) external returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, bytes memory data);
/// @dev Burn short position, withdraw token0 and token1.
/// @dev Can only be called after the maturity of the pool.
/// @param param The parameters for the collect function.
/// @return token0Amount The amount of token0 withdrawn.
/// @return token1Amount The amount of token1 withdrawn.
/// @return shortAmount The amount of short burnt.
function collect(
TimeswapV2OptionCollectParam calldata param
) external returns (uint256 token0Amount, uint256 token1Amount, uint256 shortAmount, bytes memory data);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @title The interface for the contract that deploys Timeswap V2 Option pair contracts
/// @notice The Timeswap V2 Option Factory facilitates creation of Timeswap V2 Options pair.
interface ITimeswapV2OptionFactory {
/* ===== EVENT ===== */
/// @dev Emits when a new Timeswap V2 Option contract is created.
/// @param caller The address of the caller of create function.
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param optionPair The address of the Timeswap V2 Option contract created.
event Create(address indexed caller, address indexed token0, address indexed token1, address optionPair);
/* ===== VIEW ===== */
/// @dev Returns the address of a Timeswap V2 Option.
/// @dev Returns a zero address if the Timeswap V2 Option does not exist.
/// @notice The token0 address must be smaller than token1 address.
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @return optionPair The address of the Timeswap V2 Option contract or a zero address.
function get(address token0, address token1) external view returns (address optionPair);
/// @dev Get the address of the option pair in the option pair enumeration list.
/// @param id The chosen index.
function getByIndex(uint256 id) external view returns (address optionPair);
/// @dev The number of option pairs deployed.
function numberOfPairs() external view returns (uint256);
/* ===== UPDATE ===== */
/// @dev Creates a Timeswap V2 Option based on pair parameters.
/// @dev Cannot create a duplicate Timeswap V2 Option with the same pair parameters.
/// @notice The token0 address must be smaller than token1 address.
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param optionPair The address of the Timeswap V2 Option contract created.
function create(address token0, address token1) external returns (address optionPair);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
/// @title An interface for TS-V2 Periphery Collect
interface ITimeswapV2PeripheryCollect is IERC1155Receiver {
/// @dev Returns the option factory address.
function optionFactory() external returns (address);
/// @dev Return the tokens address
function tokens() external returns (address);
/// @dev Returns the liquidity tokens address.
function liquidityTokens() external returns (address);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {ITimeswapV2PeripheryCollect} from "@timeswap-labs/v2-periphery/contracts/interfaces/ITimeswapV2PeripheryCollect.sol";
import {INativeWithdraws} from "./INativeWithdraws.sol";
import {IMulticall} from "./IMulticall.sol";
import {TimeswapV2PeripheryNoDexCollectParam} from "../structs/Param.sol";
/// @title An interface for TS-V2 Periphery No Dex Collect.
interface ITimeswapV2PeripheryNoDexCollect is ITimeswapV2PeripheryCollect, INativeWithdraws, IMulticall {
event Collect(
address indexed token0,
address indexed token1,
uint256 strike,
uint256 indexed maturity,
address to,
uint256 token0Amount,
uint256 token1Amount,
uint256 shortFees,
uint256 shortReturned,
uint256 excessShortAmount
);
error MinTokenReached(uint256 tokenAmount, uint256 minTokenAmount);
/// @dev The collect function.
/// @param param Collect param.
/// @return token0Amount
/// @return token1Amount
function collect(
TimeswapV2PeripheryNoDexCollectParam calldata param
) external returns (uint256 token0Amount, uint256 token1Amount);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {TimeswapV2TokenPosition} from "../structs/Position.sol";
import {TimeswapV2TokenMintParam, TimeswapV2TokenBurnParam} from "../structs/Param.sol";
/// @title An interface for TS-V2 token system
/// @notice This interface is used to interact with TS-V2 positions
interface ITimeswapV2Token is IERC1155 {
/// @dev Returns the factory address that deployed this contract.
function optionFactory() external view returns (address);
/// @dev Returns the position Balance of the owner
/// @param owner The owner of the token
/// @param position type of option position (long0, long1, short)
function positionOf(address owner, TimeswapV2TokenPosition calldata position) external view returns (uint256 amount);
/// @dev Transfers position token TimeswapV2Token from `from` to `to`
/// @param from The address to transfer position token from
/// @param to The address to transfer position token to
/// @param position The TimeswapV2Token Position to transfer
/// @param amount The amount of TimeswapV2Token Position to transfer
function transferTokenPositionFrom(
address from,
address to,
TimeswapV2TokenPosition calldata position,
uint256 amount
) external;
/// @dev mints TimeswapV2Token as per postion and amount
/// @param param The TimeswapV2TokenMintParam
/// @return data Arbitrary data
function mint(TimeswapV2TokenMintParam calldata param) external returns (bytes memory data);
/// @dev burns TimeswapV2Token as per postion and amount
/// @param param The TimeswapV2TokenBurnParam
function burn(TimeswapV2TokenBurnParam calldata param) external returns (bytes memory data);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title Interface for Native token
/// @dev This interface is used to interact with the native token
/// @dev The native token could be ETH for ethereum or BNB for Binance Smart Chain or MATIC for Polygon
interface IWrappedNative is IERC20 {
/// @notice Deposit native token to get wrapped native token
function deposit() external payable;
/// @notice Withdraw wrapped native token to get native token
function withdraw(uint256) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @title Library for math related utils
/// @author Timeswap Labs
library Math {
/// @dev Reverts when divide by zero.
error DivideByZero();
error Overflow();
/// @dev Add two uint256.
/// @notice May overflow.
/// @param addend1 The first addend.
/// @param addend2 The second addend.
/// @return sum The sum.
function unsafeAdd(uint256 addend1, uint256 addend2) internal pure returns (uint256 sum) {
unchecked {
sum = addend1 + addend2;
}
}
/// @dev Subtract two uint256.
/// @notice May underflow.
/// @param minuend The minuend.
/// @param subtrahend The subtrahend.
/// @return difference The difference.
function unsafeSub(uint256 minuend, uint256 subtrahend) internal pure returns (uint256 difference) {
unchecked {
difference = minuend - subtrahend;
}
}
/// @dev Multiply two uint256.
/// @notice May overflow.
/// @param multiplicand The multiplicand.
/// @param multiplier The multiplier.
/// @return product The product.
function unsafeMul(uint256 multiplicand, uint256 multiplier) internal pure returns (uint256 product) {
unchecked {
product = multiplicand * multiplier;
}
}
/// @dev Divide two uint256.
/// @notice Reverts when divide by zero.
/// @param dividend The dividend.
/// @param divisor The divisor.
//// @param roundUp Round up the result when true. Round down if false.
/// @return quotient The quotient.
function div(uint256 dividend, uint256 divisor, bool roundUp) internal pure returns (uint256 quotient) {
quotient = dividend / divisor;
if (roundUp && dividend % divisor != 0) quotient++;
}
/// @dev Shift right a uint256 number.
/// @param dividend The dividend.
/// @param divisorBit The divisor in bits.
/// @param roundUp True if ceiling the result. False if floor the result.
/// @return quotient The quotient.
function shr(uint256 dividend, uint8 divisorBit, bool roundUp) internal pure returns (uint256 quotient) {
quotient = dividend >> divisorBit;
if (roundUp && dividend % (1 << divisorBit) != 0) quotient++;
}
/// @dev Gets the square root of a value.
/// @param value The value being square rooted.
/// @param roundUp Round up the result when true. Round down if false.
/// @return result The resulting value of the square root.
function sqrt(uint256 value, bool roundUp) internal pure returns (uint256 result) {
if (value == type(uint256).max) return result = type(uint128).max;
if (value == 0) return 0;
unchecked {
uint256 estimate = (value + 1) >> 1;
result = value;
while (estimate < result) {
result = estimate;
estimate = (value / estimate + estimate) >> 1;
}
}
if (roundUp && result * result < value) result++;
}
/// @dev Gets the min of two uint256 number.
/// @param value1 The first value to be compared.
/// @param value2 The second value to be compared.
/// @return result The min result.
function min(uint256 value1, uint256 value2) internal pure returns (uint256 result) {
return value1 < value2 ? value1 : value2;
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.8.8;
import "../interfaces/IMulticall.sol";
/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall is IMulticall {
/// @inheritdoc IMulticall
function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success) {
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (result.length < 68) revert MulticallFailed("Invalid Result");
assembly {
result := add(result, 0x04)
}
revert MulticallFailed(abi.decode(result, (string)));
}
results[i] = result;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity =0.8.8;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IWrappedNative} from "../interfaces/external/IWrappedNative.sol";
import {INativeImmutableState} from "../interfaces/INativeImmutableState.sol";
import {INativeWithdraws} from "../interfaces/INativeWithdraws.sol";
import {INativePayments} from "../interfaces/INativePayments.sol";
import {NativeTransfer} from "../libraries/NativeTransfer.sol";
abstract contract NativeImmutableState is INativeImmutableState {
/// @inheritdoc INativeImmutableState
address public immutable override wrappedNativeToken;
constructor(address chosenWrappedNativeToken) {
wrappedNativeToken = chosenWrappedNativeToken;
}
}
abstract contract NativeWithdraws is INativeWithdraws, NativeImmutableState {
error CallerNotWrappedNative(address from);
error InsufficientWrappedNative(uint256 value);
receive() external payable {
if (msg.sender != wrappedNativeToken) revert CallerNotWrappedNative(msg.sender);
}
/// @inheritdoc INativeWithdraws
function unwrapWrappedNatives(uint256 amountMinimum, address recipient) external payable override {
uint256 balanceWrappedNative = IWrappedNative(wrappedNativeToken).balanceOf(address(this));
if (balanceWrappedNative < amountMinimum) revert InsufficientWrappedNative(balanceWrappedNative);
if (balanceWrappedNative != 0) {
IWrappedNative(wrappedNativeToken).withdraw(balanceWrappedNative);
NativeTransfer.safeTransferNatives(recipient, balanceWrappedNative);
}
}
}
abstract contract NativePayments is INativePayments, NativeImmutableState {
using SafeERC20 for IERC20;
/// @inheritdoc INativePayments
function refundNatives() external payable override {
if (address(this).balance > 0) NativeTransfer.safeTransferNatives(msg.sender, address(this).balance);
}
/// @param token The token to pay
/// @param payer The entity that must pay
/// @param recipient The entity that will receive payment
/// @param value The amount to pay
function pay(address token, address payer, address recipient, uint256 value) internal {
if (token == wrappedNativeToken && address(this).balance >= value) {
// pay with WrappedNative
IWrappedNative(wrappedNativeToken).deposit{value: value}();
IERC20(token).safeTransfer(recipient, value);
} else {
// pull payment
IERC20(token).safeTransferFrom(payer, recipient, value);
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
library NativeTransfer {
error NativeTransferFailed(address to, uint256 value);
/// @notice Transfers Natives to the recipient address
/// @dev Reverts if the transfer fails
/// @param to The destination of the transfer
/// @param value The value to be transferred
function safeTransferNatives(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
if (!success) {
revert NativeTransferFailed(to, value);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity =0.8.8;
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
abstract contract OnlyOperatorReceiver is IERC1155Receiver {
function onERC1155Received(
address operator,
address,
uint256,
uint256,
bytes memory
) external view override returns (bytes4) {
if (operator != address(this)) return bytes4("");
else return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) external pure override returns (bytes4) {
return bytes4("");
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";
import {OptionPairLibrary} from "./OptionPair.sol";
import {ITimeswapV2OptionFactory} from "../interfaces/ITimeswapV2OptionFactory.sol";
/// @title library for option utils
/// @author Timeswap Labs
library OptionFactoryLibrary {
using OptionPairLibrary for address;
/// @dev reverts if the factory is the zero address.
error ZeroFactoryAddress();
/// @dev check if the factory address is not zero.
/// @param optionFactory The factory address.
function checkNotZeroFactory(address optionFactory) internal pure {
if (optionFactory == address(0)) revert ZeroFactoryAddress();
}
/// @dev Helper function to get the option pair address.
/// @param optionFactory The address of the option factory.
/// @param token0 The smaller ERC20 address of the pair.
/// @param token1 The larger ERC20 address of the pair.
/// @return optionPair The result option pair address.
function get(address optionFactory, address token0, address token1) internal view returns (address optionPair) {
optionPair = ITimeswapV2OptionFactory(optionFactory).get(token0, token1);
}
/// @dev Helper function to get the option pair address.
/// @notice reverts when the option pair does not exist.
/// @param optionFactory The address of the option factory.
/// @param token0 The smaller ERC20 address of the pair.
/// @param token1 The larger ERC20 address of the pair.
/// @return optionPair The result option pair address.
function getWithCheck(
address optionFactory,
address token0,
address token1
) internal view returns (address optionPair) {
optionPair = get(optionFactory, token0, token1);
if (optionPair == address(0)) Error.zeroAddress();
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @title library for optionPair utils
/// @author Timeswap Labs
library OptionPairLibrary {
/// @dev Reverts when option address is zero.
error ZeroOptionAddress();
/// @dev Reverts when the pair has incorrect format.
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
error InvalidOptionPair(address token0, address token1);
/// @dev Reverts when the Timeswap V2 Option already exist.
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param optionPair The address of the existed Pair contract.
error OptionPairAlreadyExisted(address token0, address token1, address optionPair);
/// @dev Checks if option address is not zero.
/// @param optionPair The option pair address being inquired.
function checkNotZeroAddress(address optionPair) internal pure {
if (optionPair == address(0)) revert ZeroOptionAddress();
}
/// @dev Check if the pair tokens is in correct format.
/// @notice Reverts if token0 is greater than or equal token1.
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
function checkCorrectFormat(address token0, address token1) internal pure {
if (token0 >= token1) revert InvalidOptionPair(token0, token1);
}
/// @dev Check if the pair already existed.
/// @notice Reverts if the pair is not a zero address.
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param optionPair The address of the existed Pair contract.
function checkDoesNotExist(address token0, address token1, address optionPair) internal pure {
if (optionPair != address(0)) revert OptionPairAlreadyExisted(token0, token1, optionPair);
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";
/// @dev parameter for minting Timeswap V2 Tokens
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param long0To The address of the recipient of TimeswapV2Token representing long0 position.
/// @param long1To The address of the recipient of TimeswapV2Token representing long1 position.
/// @param shortTo The address of the recipient of TimeswapV2Token representing short position.
/// @param long0Amount The amount of long0 deposited.
/// @param long1Amount The amount of long1 deposited.
/// @param shortAmount The amount of short deposited.
/// @param data Arbitrary data passed to the callback.
struct TimeswapV2TokenMintParam {
address token0;
address token1;
uint256 strike;
uint256 maturity;
address long0To;
address long1To;
address shortTo;
uint256 long0Amount;
uint256 long1Amount;
uint256 shortAmount;
bytes data;
}
/// @dev parameter for burning Timeswap V2 Tokens
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param long0To The address of the recipient of long token0 position.
/// @param long1To The address of the recipient of long token1 position.
/// @param shortTo The address of the recipient of short position.
/// @param long0Amount The amount of TimeswapV2Token long0 deposited and equivalent long0 position is withdrawn.
/// @param long1Amount The amount of TimeswapV2Token long1 deposited and equivalent long1 position is withdrawn.
/// @param shortAmount The amount of TimeswapV2Token short deposited and equivalent short position is withdrawn,
/// @param data Arbitrary data passed to the callback, initalize as empty if not required.
struct TimeswapV2TokenBurnParam {
address token0;
address token1;
uint256 strike;
uint256 maturity;
address long0To;
address long1To;
address shortTo;
uint256 long0Amount;
uint256 long1Amount;
uint256 shortAmount;
bytes data;
}
/// @dev parameter for minting Timeswap V2 Liquidity Tokens
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param to The address of the recipient of TimeswapV2LiquidityToken.
/// @param liquidityAmount The amount of liquidity token deposited.
/// @param data Arbitrary data passed to the callback.
/// @param erc1155Data Arbitrary custojm data passed through erc115 minting.
struct TimeswapV2LiquidityTokenMintParam {
address token0;
address token1;
uint256 strike;
uint256 maturity;
address to;
uint160 liquidityAmount;
bytes data;
bytes erc1155Data;
}
/// @dev parameter for burning Timeswap V2 Liquidity Tokens
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param to The address of the recipient of the liquidity token.
/// @param liquidityAmount The amount of liquidity token withdrawn.
/// @param data Arbitrary data passed to the callback, initalize as empty if not required.
struct TimeswapV2LiquidityTokenBurnParam {
address token0;
address token1;
uint256 strike;
uint256 maturity;
address to;
uint160 liquidityAmount;
bytes data;
}
/// @dev parameter for collecting fees and shortReturned from Timeswap V2 Liquidity Tokens
/// @param token0 The first ERC20 token address of the pair.
/// @param token1 The second ERC20 token address of the pair.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param from The address of the owner of the fees and shortReturned;
/// @param long0FeesTo The address of the recipient of the long0 fees.
/// @param long1FeesTo The address of the recipient of the long1 fees.
/// @param shortFeesTo The address of the recipient of the short fees.
/// @param shortReturnedTo The address of the recipient of the short returned.
/// @param long0FeesDesired The maximum amount of long0Fees desired to be withdrawn.
/// @param long1FeesDesired The maximum amount of long1Fees desired to be withdrawn.
/// @param shortFeesDesired The maximum amount of shortFees desired to be withdrawn.
/// @param shortReturnedDesired The maximum amount of shortReturned desired to be withdrawn.
/// @param data Arbitrary data passed to the callback, initalize as empty if not required.
struct TimeswapV2LiquidityTokenCollectParam {
address token0;
address token1;
uint256 strike;
uint256 maturity;
address from;
address long0FeesTo;
address long1FeesTo;
address shortFeesTo;
address shortReturnedTo;
uint256 long0FeesDesired;
uint256 long1FeesDesired;
uint256 shortFeesDesired;
uint256 shortReturnedDesired;
bytes data;
}
library ParamLibrary {
/// @dev Sanity checks for token mint.
function check(TimeswapV2TokenMintParam memory param) internal pure {
if (param.long0To == address(0) || param.long1To == address(0) || param.shortTo == address(0)) Error.zeroAddress();
if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
if (param.long0Amount == 0 && param.long1Amount == 0 && param.shortAmount == 0) Error.zeroInput();
}
/// @dev Sanity checks for token burn.
function check(TimeswapV2TokenBurnParam memory param) internal pure {
if (param.long0To == address(0) || param.long1To == address(0) || param.shortTo == address(0)) Error.zeroAddress();
if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
if (param.long0Amount == 0 && param.long1Amount == 0 && param.shortAmount == 0) Error.zeroInput();
}
/// @dev Sanity checks for liquidity token mint.
function check(TimeswapV2LiquidityTokenMintParam memory param) internal pure {
if (param.to == address(0)) Error.zeroAddress();
if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
if (param.liquidityAmount == 0) Error.zeroInput();
}
/// @dev Sanity checks for liquidity token burn.
function check(TimeswapV2LiquidityTokenBurnParam memory param) internal pure {
if (param.to == address(0)) Error.zeroAddress();
if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
if (param.liquidityAmount == 0) Error.zeroInput();
}
/// @dev Sanity checks for liquidity token collect.
function check(TimeswapV2LiquidityTokenCollectParam memory param) internal pure {
if (
param.from == address(0) ||
param.long0FeesTo == address(0) ||
param.long1FeesTo == address(0) ||
param.shortFeesTo == address(0) ||
param.shortReturnedTo == address(0)
) Error.zeroAddress();
if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
if (
param.long0FeesDesired == 0 &&
param.long1FeesDesired == 0 &&
param.shortFeesDesired == 0 &&
param.shortReturnedDesired == 0
) Error.zeroInput();
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @dev The three type of native token positions.
/// @dev Long0 is denominated as the underlying Token0.
/// @dev Long1 is denominated as the underlying Token1.
/// @dev When strike greater than uint128 then Short is denominated as Token0 (the base token denomination).
/// @dev When strike is uint128 then Short is denominated as Token1 (the base token denomination).
enum TimeswapV2OptionPosition {
Long0,
Long1,
Short
}
/// @title library for position utils
/// @author Timeswap Labs
/// @dev Helper functions for the TimeswapOptionPosition enum.
library PositionLibrary {
/// @dev Reverts when the given type of position is invalid.
error InvalidPosition();
/// @dev Checks that the position input is correct.
/// @param position The position input.
function check(TimeswapV2OptionPosition position) internal pure {
if (uint256(position) >= 3) revert InvalidPosition();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (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;
/**
* @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.encodeWithSelector(token.transfer.selector, 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.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));
}
/**
* @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);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @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.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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).
*
* 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.isContract(address(token));
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @dev A data with strike and maturity data.
/// @param strike The strike.
/// @param maturity The maturity.
struct StrikeAndMaturity {
uint256 strike;
uint256 maturity;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {Math} from "@timeswap-labs/v2-library/contracts/Math.sol";
import {ERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol";
import {ITimeswapV2OptionFactory} from "@timeswap-labs/v2-option/contracts/interfaces/ITimeswapV2OptionFactory.sol";
import {ITimeswapV2Option} from "@timeswap-labs/v2-option/contracts/interfaces/ITimeswapV2Option.sol";
import {OptionFactoryLibrary} from "@timeswap-labs/v2-option/contracts/libraries/OptionFactory.sol";
import {TimeswapV2OptionCollectParam} from "@timeswap-labs/v2-option/contracts/structs/Param.sol";
import {TimeswapV2OptionCollect} from "@timeswap-labs/v2-option/contracts/enums/Transaction.sol";
import {ITimeswapV2Token} from "@timeswap-labs/v2-token/contracts/interfaces/ITimeswapV2Token.sol";
import {TimeswapV2TokenBurnParam} from "@timeswap-labs/v2-token/contracts/structs/Param.sol";
import {ITimeswapV2LiquidityToken} from "@timeswap-labs/v2-token/contracts/interfaces/ITimeswapV2LiquidityToken.sol";
import {TimeswapV2LiquidityTokenPosition} from "@timeswap-labs/v2-token/contracts/structs/Position.sol";
import {TimeswapV2TokenBurnParam, TimeswapV2LiquidityTokenCollectParam} from "@timeswap-labs/v2-token/contracts/structs/Param.sol";
import {TimeswapV2PeripheryCollectParam} from "./structs/Param.sol";
import {ITimeswapV2PeripheryCollect} from "./interfaces/ITimeswapV2PeripheryCollect.sol";
abstract contract TimeswapV2PeripheryCollect is ITimeswapV2PeripheryCollect, ERC1155Receiver {
using Math for uint256;
/* ===== MODEL ===== */
/// @inheritdoc ITimeswapV2PeripheryCollect
address public immutable override optionFactory;
/// @inheritdoc ITimeswapV2PeripheryCollect
address public immutable override tokens;
/// @inheritdoc ITimeswapV2PeripheryCollect
address public immutable override liquidityTokens;
/* ===== INIT ===== */
constructor(address chosenOptionFactory, address chosenTokens, address chosenLiquidityTokens) {
optionFactory = chosenOptionFactory;
tokens = chosenTokens;
liquidityTokens = chosenLiquidityTokens;
}
/// @notice the abstract implementation for collect function
/// @param param for collect as mentioned in the TimeswapV2PeripheryCollectParam struct
/// @return token0Amount is the token0Amount recieved
/// @return token1Amount is the token1Amount recieved
function collect(
TimeswapV2PeripheryCollectParam memory param
)
internal
returns (uint256 token0Amount, uint256 token1Amount, uint256 shortFeesWithdrawn, uint256 shortReturnedWithdrawn)
{
// Get the amount of short fees and short returned the msg.sender has
(, , shortFeesWithdrawn, shortReturnedWithdrawn) = ITimeswapV2LiquidityToken(liquidityTokens)
.feesEarnedAndShortReturnedOf(
msg.sender,
TimeswapV2LiquidityTokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity
})
);
uint256 shortAmount;
// Include the short fees and short returned to the total amount of short to burn and withdraw the base ERC20
if (shortFeesWithdrawn != 0 || shortReturnedWithdrawn != 0) {
uint256 shortReturnedAmount;
(, , shortAmount, shortReturnedAmount, ) = ITimeswapV2LiquidityToken(liquidityTokens).collect(
TimeswapV2LiquidityTokenCollectParam({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
from: msg.sender,
long0FeesTo: address(this),
long1FeesTo: address(this),
shortFeesTo: address(this),
shortReturnedTo: address(this),
long0FeesDesired: 0,
long1FeesDesired: 0,
shortFeesDesired: shortFeesWithdrawn,
shortReturnedDesired: shortReturnedWithdrawn,
data: bytes("")
})
);
shortAmount += shortReturnedAmount;
}
// If there is any excess short amount, unwrap it first to get the short position
if (param.excessShortAmount != 0) {
ITimeswapV2Token(tokens).burn(
TimeswapV2TokenBurnParam({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
long0To: address(this),
long1To: address(this),
shortTo: address(this),
long0Amount: 0,
long1Amount: 0,
shortAmount: param.excessShortAmount,
data: bytes("")
})
);
shortAmount += param.excessShortAmount;
}
address optionPair = OptionFactoryLibrary.getWithCheck(optionFactory, param.token0, param.token1);
// shortAmount = ITimeswapV2Option(optionPair).positionOf(
// param.strike,
// param.maturity,
// address(this),
// TimeswapV2OptionPosition.Short
// );
// Collect the underlying ERC20 token by burning the short total
(token0Amount, token1Amount, , ) = ITimeswapV2Option(optionPair).collect(
TimeswapV2OptionCollectParam({
strike: param.strike,
maturity: param.maturity,
token0To: param.token0To,
token1To: param.token1To,
transaction: TimeswapV2OptionCollect.GivenShort,
amount: shortAmount,
data: bytes("")
})
);
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";
import {TimeswapV2OptionPosition} from "@timeswap-labs/v2-option/contracts/enums/Position.sol";
import {ITimeswapV2Token} from "@timeswap-labs/v2-token/contracts/interfaces/ITimeswapV2Token.sol";
import {ITimeswapV2LiquidityToken} from "@timeswap-labs/v2-token/contracts/interfaces/ITimeswapV2LiquidityToken.sol";
import {TimeswapV2TokenPosition, TimeswapV2LiquidityTokenPosition} from "@timeswap-labs/v2-token/contracts/structs/Position.sol";
import {TimeswapV2PeripheryCollect} from "@timeswap-labs/v2-periphery/contracts/TimeswapV2PeripheryCollect.sol";
import {TimeswapV2PeripheryCollectParam} from "@timeswap-labs/v2-periphery/contracts/structs/Param.sol";
import {ITimeswapV2PeripheryNoDexCollect} from "./interfaces/ITimeswapV2PeripheryNoDexCollect.sol";
import {TimeswapV2PeripheryNoDexCollectParam} from "./structs/Param.sol";
import {OnlyOperatorReceiver} from "./base/OnlyOperatorReceiver.sol";
import {NativeImmutableState, NativeWithdraws} from "./base/Native.sol";
import {Multicall} from "./base/Multicall.sol";
/// @author Timeswap Labs
contract TimeswapV2PeripheryNoDexCollect is
TimeswapV2PeripheryCollect,
ITimeswapV2PeripheryNoDexCollect,
OnlyOperatorReceiver,
NativeImmutableState,
NativeWithdraws,
Multicall
{
constructor(
address chosenOptionFactory,
address chosenTokens,
address chosenLiquidityTokens,
address chosenNative
)
TimeswapV2PeripheryCollect(chosenOptionFactory, chosenTokens, chosenLiquidityTokens)
NativeImmutableState(chosenNative)
{}
function collect(
TimeswapV2PeripheryNoDexCollectParam calldata param
) external returns (uint256 token0Amount, uint256 token1Amount) {
if (param.deadline < block.timestamp) Error.deadlineReached(param.deadline);
ITimeswapV2Token(tokens).transferTokenPositionFrom(
msg.sender,
address(this),
TimeswapV2TokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
position: TimeswapV2OptionPosition.Short
}),
param.excessShortAmount
);
(, , uint256 shortFees, uint256 shortReturned) = ITimeswapV2LiquidityToken(liquidityTokens)
.feesEarnedAndShortReturnedOf(
msg.sender,
TimeswapV2LiquidityTokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity
})
);
(token0Amount, token1Amount, , ) = collect(
TimeswapV2PeripheryCollectParam({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
token0To: param.to,
token1To: param.to,
excessShortAmount: param.excessShortAmount
})
);
if (token0Amount < param.minToken0Amount) revert MinTokenReached(token0Amount, param.minToken0Amount);
if (token1Amount < param.minToken1Amount) revert MinTokenReached(token1Amount, param.minToken1Amount);
emit Collect(
param.token0,
param.token1,
param.strike,
param.maturity,
param.to,
token0Amount,
token1Amount,
shortFees,
shortReturned,
param.excessShortAmount
);
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;
/// @dev The different input for the mint transaction.
enum TimeswapV2OptionMint {
GivenTokensAndLongs,
GivenShorts
}
/// @dev The different input for the burn transaction.
enum TimeswapV2OptionBurn {
GivenTokensAndLongs,
GivenShorts
}
/// @dev The different input for the swap transaction.
enum TimeswapV2OptionSwap {
GivenToken0AndLong0,
GivenToken1AndLong1
}
/// @dev The different input for the collect transaction.
enum TimeswapV2OptionCollect {
GivenShort,
GivenToken0,
GivenToken1
}
/// @title library for transaction checks
/// @author Timeswap Labs
/// @dev Helper functions for the all enums in this module.
library TransactionLibrary {
/// @dev Reverts when the given type of transaction is invalid.
error InvalidTransaction();
/// @dev checks that the given input is correct.
/// @param transaction the mint transaction input.
function check(TimeswapV2OptionMint transaction) internal pure {
if (uint256(transaction) >= 2) revert InvalidTransaction();
}
/// @dev checks that the given input is correct.
/// @param transaction the burn transaction input.
function check(TimeswapV2OptionBurn transaction) internal pure {
if (uint256(transaction) >= 2) revert InvalidTransaction();
}
/// @dev checks that the given input is correct.
/// @param transaction the swap transaction input.
function check(TimeswapV2OptionSwap transaction) internal pure {
if (uint256(transaction) >= 2) revert InvalidTransaction();
}
/// @dev checks that the given input is correct.
/// @param transaction the collect transaction input.
function check(TimeswapV2OptionCollect transaction) internal pure {
if (uint256(transaction) >= 3) revert InvalidTransaction();
}
}
{
"compilationTarget": {
"@timeswap-labs/v2-periphery-nodex/contracts/TimeswapV2PeripheryNoDexCollect.sol": "TimeswapV2PeripheryNoDexCollect"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"chosenOptionFactory","type":"address"},{"internalType":"address","name":"chosenTokens","type":"address"},{"internalType":"address","name":"chosenLiquidityTokens","type":"address"},{"internalType":"address","name":"chosenNative","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"CallerNotWrappedNative","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"DeadlineReached","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"InsufficientWrappedNative","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"minTokenAmount","type":"uint256"}],"name":"MinTokenReached","type":"error"},{"inputs":[{"internalType":"string","name":"revertString","type":"string"}],"name":"MulticallFailed","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"NativeTransferFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"token0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shortFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shortReturned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"excessShortAmount","type":"uint256"}],"name":"Collect","type":"event"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"isToken0","type":"bool"},{"internalType":"uint256","name":"excessShortAmount","type":"uint256"},{"internalType":"uint256","name":"minToken0Amount","type":"uint256"},{"internalType":"uint256","name":"minToken1Amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct TimeswapV2PeripheryNoDexCollectParam","name":"param","type":"tuple"}],"name":"collect","outputs":[{"internalType":"uint256","name":"token0Amount","type":"uint256"},{"internalType":"uint256","name":"token1Amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidityTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unwrapWrappedNatives","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"wrappedNativeToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]