// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/Address.sol)pragmasolidity ^0.8.1;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @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.
* ====
*/functionisContract(address account) internalviewreturns (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://diligence.consensys.net/posts/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].
*/functionsendValue(addresspayable 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._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
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._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
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._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
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._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
(bool success, bytesmemory 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._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
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._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
(bool success, bytesmemory 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._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
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._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
(bool success, bytesmemory 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._
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
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 contractrequire(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._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function_revert(bytesmemory returndata, stringmemory errorMessage) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Contract Source Code
File 2 of 10: ECDSA.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/ECDSA.sol)pragmasolidity ^0.8.0;import"../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/libraryECDSA{
enumRecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function_throwError(RecoverError error) privatepure{
if (error == RecoverError.NoError) {
return; // no error: do nothing
} elseif (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} elseif (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} elseif (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/functiontryRecover(bytes32 hash, bytesmemory signature) internalpurereturns (address, RecoverError) {
if (signature.length==65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them// currently is to use assembly./// @solidity memory-safe-assemblyassembly {
r :=mload(add(signature, 0x20))
s :=mload(add(signature, 0x40))
v :=byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/functionrecover(bytes32 hash, bytesmemory signature) internalpurereturns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/functiontryRecover(bytes32 hash,
bytes32 r,
bytes32 vs
) internalpurereturns (address, RecoverError) {
bytes32 s = vs &bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v =uint8((uint256(vs) >>255) +27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/functionrecover(bytes32 hash,
bytes32 r,
bytes32 vs
) internalpurereturns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/functiontryRecover(bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internalpurereturns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most// signatures from current libraries generate a unique signature with an s-value in the lower half order.//// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept// these malleable signatures as well.if (uint256(s) >0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer addressaddress signer =ecrecover(hash, v, r, s);
if (signer ==address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/functionrecover(bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internalpurereturns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/functiontoEthSignedMessageHash(bytes32 hash) internalpurereturns (bytes32) {
// 32 is the length in bytes of hash,// enforced by the type signature abovereturnkeccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/functiontoEthSignedMessageHash(bytesmemory s) internalpurereturns (bytes32) {
returnkeccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/functiontoTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internalpurereturns (bytes32) {
returnkeccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
Contract Source Code
File 3 of 10: EIP712.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/EIP712.sol)pragmasolidity ^0.8.0;import"./ECDSA.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/abstractcontractEIP712{
/* solhint-disable var-name-mixedcase */// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to// invalidate the cached domain separator if the chain id changes.bytes32privateimmutable _CACHED_DOMAIN_SEPARATOR;
uint256privateimmutable _CACHED_CHAIN_ID;
addressprivateimmutable _CACHED_THIS;
bytes32privateimmutable _HASHED_NAME;
bytes32privateimmutable _HASHED_VERSION;
bytes32privateimmutable _TYPE_HASH;
/* solhint-enable var-name-mixedcase *//**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/constructor(stringmemory name, stringmemory version) {
bytes32 hashedName =keccak256(bytes(name));
bytes32 hashedVersion =keccak256(bytes(version));
bytes32 typeHash =keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID =block.chainid;
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
_CACHED_THIS =address(this);
_TYPE_HASH = typeHash;
}
/**
* @dev Returns the domain separator for the current chain.
*/function_domainSeparatorV4() internalviewreturns (bytes32) {
if (address(this) == _CACHED_THIS &&block.chainid== _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
}
}
function_buildDomainSeparator(bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) privateviewreturns (bytes32) {
returnkeccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/function_hashTypedDataV4(bytes32 structHash) internalviewvirtualreturns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
}
Contract Source Code
File 4 of 10: IERC1271.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/interfaceIERC1271{
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/functionisValidSignature(bytes32 hash, bytesmemory signature) externalviewreturns (bytes4 magicValue);
}
Contract Source Code
File 5 of 10: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed 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.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (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.
*/functiontransfer(address to, uint256 amount) externalreturns (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.
*/functionallowance(address owner, address spender) externalviewreturns (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.
*/functionapprove(address spender, uint256 amount) externalreturns (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.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) externalreturns (bool);
}
Contract Source Code
File 6 of 10: LibOrder.sol
//SPDX-License-Identifier: MITpragmasolidity ^0.8.0;libraryLibOrder{
//keccak256("Order(address user,address sellToken,address buyToken,uint256 sellAmount,uint256 buyAmount,uint256 expirationTimeSeconds)")bytes32internalconstant _EIP712_ORDER_SCHEMA_HASH =0x68d868c8698fc31da3a36bb7a184a4af099797794701bae97bea3de7ebe6e399;
structOrder {
address user; //address of the Order Creator making the saleaddress sellToken; // address of the Token the Order Creator wants to selladdress buyToken; // address of the Token the Order Creator wants to receive in returnuint256 sellAmount; // amount of Token that the Order Creator wants to selluint256 buyAmount; // amount of Token that the Order Creator wants to receive in returnuint256 expirationTimeSeconds; //time after which the order is no longer valid
}
structOrderInfo {
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).uint256 orderSellFilledAmount; // Amount of order that has already been filled.
}
// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstructfunctiongetContentHash(Order memory order) internalpurereturns (bytes32 orderHash) {
orderHash =keccak256(
abi.encode(_EIP712_ORDER_SCHEMA_HASH, order.user, order.sellToken, order.buyToken, order.sellAmount, order.buyAmount, order.expirationTimeSeconds)
);
}
}
Contract Source Code
File 7 of 10: Math.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/math/Math.sol)pragmasolidity ^0.8.0;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
enumRounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b) / 2 can overflow.return (a & b) + (a ^ b) /2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.return a ==0 ? 0 : (a -1) / b +1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/functionmulDiv(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256// variables such that product = prod1 * 2^256 + prod0.uint256 prod0; // Least significant 256 bits of the productuint256 prod1; // Most significant 256 bits of the productassembly {
let mm :=mulmod(x, y, not(0))
prod0 :=mul(x, y)
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.if (prod1 ==0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.require(denominator > prod1);
///////////////////////////////////////////////// 512 by 256 division.///////////////////////////////////////////////// Make division exact by subtracting the remainder from [prod1 prod0].uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.// See https://cs.stackexchange.com/q/138556/92363.// Does not overflow because the denominator cannot be zero at this stage in the function.uint256 twos = denominator & (~denominator +1);
assembly {
// Divide denominator by twos.
denominator :=div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 :=div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos :=add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for// four bits. That is, denominator * inv = 1 mod 2^4.uint256 inverse = (3* denominator) ^2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works// in modular arithmetic, doubling the correct bits in each step.
inverse *=2- denominator * inverse; // inverse mod 2^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // inverse mod 2^256// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/functionmulDiv(uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internalpurereturns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up &&mulmod(x, y, denominator) >0) {
result +=1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.//// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.//// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`//// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.uint256 result =1<< (log2(a) >>1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision// into the expected uint128 result.unchecked {
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/functionsqrt(uint256 a, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=128;
}
if (value >>64>0) {
value >>=64;
result +=64;
}
if (value >>32>0) {
value >>=32;
result +=32;
}
if (value >>16>0) {
value >>=16;
result +=16;
}
if (value >>8>0) {
value >>=8;
result +=8;
}
if (value >>4>0) {
value >>=4;
result +=4;
}
if (value >>2>0) {
value >>=2;
result +=2;
}
if (value >>1>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result =log2(value);
return result + (rounding == Rounding.Up &&1<< result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >=10**64) {
value /=10**64;
result +=64;
}
if (value >=10**32) {
value /=10**32;
result +=32;
}
if (value >=10**16) {
value /=10**16;
result +=16;
}
if (value >=10**8) {
value /=10**8;
result +=8;
}
if (value >=10**4) {
value /=10**4;
result +=4;
}
if (value >=10**2) {
value /=10**2;
result +=2;
}
if (value >=10**1) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up &&10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/functionlog256(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=16;
}
if (value >>64>0) {
value >>=64;
result +=8;
}
if (value >>32>0) {
value >>=32;
result +=4;
}
if (value >>16>0) {
value >>=16;
result +=2;
}
if (value >>8>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog256(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up &&1<< (result *8) < value ? 1 : 0);
}
}
}
Contract Source Code
File 8 of 10: SignatureChecker.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/SignatureChecker.sol)pragmasolidity ^0.8.0;import"./ECDSA.sol";
import"../Address.sol";
import"../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Gnosis Safe.
*
* _Available since v4.1._
*/librarySignatureChecker{
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/functionisValidSignatureNow(address signer,
bytes32 hash,
bytesmemory signature
) internalviewreturns (bool) {
(address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
if (error == ECDSA.RecoverError.NoError && recovered == signer) {
returntrue;
}
(bool success, bytesmemory result) = signer.staticcall(
abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
);
return (success &&
result.length==32&&abi.decode(result, (bytes32)) ==bytes32(IERC1271.isValidSignature.selector));
}
}
Contract Source Code
File 9 of 10: Strings.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/Strings.sol)pragmasolidity ^0.8.0;import"./math/Math.sol";
/**
* @dev String operations.
*/libraryStrings{
bytes16privateconstant _SYMBOLS ="0123456789abcdef";
uint8privateconstant _ADDRESS_LENGTH =20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/functiontoString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
uint256 length = Math.log10(value) +1;
stringmemory buffer =newstring(length);
uint256 ptr;
/// @solidity memory-safe-assemblyassembly {
ptr :=add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assemblyassembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /=10;
if (value ==0) break;
}
return buffer;
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/functiontoHexString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
return toHexString(value, Math.log256(value) +1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
bytesmemory buffer =newbytes(2* length +2);
buffer[0] ="0";
buffer[1] ="x";
for (uint256 i =2* length +1; i >1; --i) {
buffer[i] = _SYMBOLS[value &0xf];
value >>=4;
}
require(value ==0, "Strings: hex length insufficient");
returnstring(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/functiontoHexString(address addr) internalpurereturns (stringmemory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
Contract Source Code
File 10 of 10: ZigZagExchange.sol
//SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import'./LibOrder.sol';
import { IERC20 } from'@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { EIP712 } from'@openzeppelin/contracts/utils/cryptography/EIP712.sol';
import { SignatureChecker } from'@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol';
// import "hardhat/console.sol";interfaceIWETH9{
functiondepositTo(address) externalpayable;
functionwithdrawTo(address, uint256) external;
functionbalanceOf(address) externalviewreturns (uint256);
}
contractZigZagExchangeisEIP712{
eventSwap(address maker,
addressindexed taker,
addressindexed makerSellToken,
addressindexed takerSellToken,
uint256 makerSellAmount,
uint256 takerSellAmount
);
eventCancelOrder(bytes32indexed orderHash);
eventOrderStatus(bytes32indexed orderHash, uint filled, uint remaining);
mapping(bytes32=>uint256) public filled;
mapping(bytes32=>bool) public cancelled;
addressimmutable WETH_ADDRESS;
addressimmutable EXCHANGE_ADDRESS;
addressconstant ETH_ADDRESS =address(0);
// initialize fee addressconstructor(stringmemory name, stringmemory version, address weth_address) EIP712(name, version) {
WETH_ADDRESS = weth_address;
EXCHANGE_ADDRESS =address(this);
}
receive() externalpayable{}
/// @notice Cancel an order so it can no longer be filled/// @param order order that should get cancelledfunctioncancelOrder(LibOrder.Order calldata order) public{
require(msg.sender== order.user, 'only user may cancel order');
bytes32 orderHash = getOrderHash(order);
require(filled[orderHash] < order.sellAmount, 'order already filled');
cancelled[orderHash] =true;
emit CancelOrder(orderHash);
}
functionfillOrderBookETH(
LibOrder.Order[] calldata makerOrder,
bytes[] calldata makerSignature,
uint takerAmount
) publicpayablereturns (bool) {
require(makerOrder.length== makerSignature.length, 'Length of makerOrders and makerSignatures does not match');
require(makerOrder.length>0, 'Length of makerOrders can not be 0');
uint256 n = makerOrder.length-1;
for (uint i =0; i <= n && takerAmount >0; i++) {
takerAmount -= _fillOrderETH(makerOrder[i], makerSignature[i], msg.sender, msg.sender, takerAmount, true);
}
require(takerAmount ==0, 'Taker amount not filled');
_refundETH();
returntrue;
}
functionfillOrderBook(LibOrder.Order[] calldata makerOrder, bytes[] calldata makerSignature, uint takerAmount) publicreturns (bool) {
require(makerOrder.length== makerSignature.length, 'Length of makerOrders and makerSignatures does not match');
require(makerOrder.length>0, 'Length of makerOrders can not be 0');
uint256 n = makerOrder.length-1;
for (uint i =0; i <= n && takerAmount >0; i++) {
takerAmount -= _fillOrder(
makerOrder[i],
makerSignature[i],
msg.sender,
msg.sender,
makerOrder[i].sellToken,
makerOrder[i].buyToken,
takerAmount,
true
);
}
require(takerAmount ==0, 'Taker amount not filled');
returntrue;
}
functionfillOrderRouteETH(
LibOrder.Order[] calldata makerOrder,
bytes[] calldata makerSignature,
uint takerAmount,
bool fillAvailable
) publicpayablereturns (bool) {
require(makerOrder.length== makerSignature.length, 'Length of makerOrders and makerSignatures does not match');
require(makerOrder.length>0, 'Length of makerOrders can not be 0');
if (makerOrder.length==1) {
return fillOrderExactInputETH(makerOrder[0], makerSignature[0], takerAmount, fillAvailable);
}
for (uint i =0; i < makerOrder.length; i++) {
require(i ==0|| makerOrder[i -1].sellToken == makerOrder[i].buyToken, 'Tokens on route do not match');
// takerAmountOut = takerAmountIn * price
takerAmount = (takerAmount * makerOrder[i].sellAmount) / makerOrder[i].buyAmount;
// first or last tx might need to (un-)wrap ETHif (i ==0&& makerOrder[0].buyToken == WETH_ADDRESS) {
takerAmount = _fillOrderETH(makerOrder[0], makerSignature[0], msg.sender, EXCHANGE_ADDRESS, takerAmount, fillAvailable);
} elseif (i == makerOrder.length-1&& makerOrder[makerOrder.length-1].sellToken == WETH_ADDRESS) {
takerAmount = _fillOrderETH(
makerOrder[makerOrder.length-1],
makerSignature[makerOrder.length-1],
EXCHANGE_ADDRESS,
msg.sender,
takerAmount,
fillAvailable
);
} else {
takerAmount = _fillOrder(
makerOrder[i],
makerSignature[i],
i ==0 ? msg.sender : EXCHANGE_ADDRESS,
i == makerOrder.length-1 ? msg.sender : EXCHANGE_ADDRESS,
makerOrder[i].sellToken,
makerOrder[i].buyToken,
takerAmount,
fillAvailable
);
}
}
_refundETH();
returntrue;
}
functionfillOrderRoute(
LibOrder.Order[] calldata makerOrder,
bytes[] calldata makerSignature,
uint takerAmount,
bool fillAvailable
) publicpayablereturns (bool) {
require(makerOrder.length== makerSignature.length, 'Length of makerOrders and makerSignatures does not match');
require(makerOrder.length>0, 'Length of makerOrders can not be 0');
if (makerOrder.length==1) {
return fillOrderExactInput(makerOrder[0], makerSignature[0], takerAmount, fillAvailable);
}
for (uint i =0; i < makerOrder.length; i++) {
require(i ==0|| makerOrder[i -1].sellToken == makerOrder[i].buyToken, 'Tokens on route do not match');
// takerAmountOut = takerAmountIn * price
takerAmount = (takerAmount * makerOrder[i].sellAmount) / makerOrder[i].buyAmount;
takerAmount = _fillOrder(
makerOrder[i],
makerSignature[i],
i ==0 ? msg.sender : EXCHANGE_ADDRESS,
i == makerOrder.length-1 ? msg.sender : EXCHANGE_ADDRESS,
makerOrder[i].sellToken,
makerOrder[i].buyToken,
takerAmount,
fillAvailable
);
}
returntrue;
}
/// @notice Fills an order with an exact amount to sell, taking or returning ETH/// @param makerOrder Order that will be used to make this swap, buyToken or sellToken must be WETH/// @param makerSignature Signature for the order used/// @param takerSellAmount amount send from the sender to the maker/// @return returns true if successfullfunctionfillOrderExactInputETH(
LibOrder.Order calldata makerOrder,
bytescalldata makerSignature,
uint takerSellAmount,
bool fillAvailable
) publicpayablereturns (bool) {
uint takerBuyAmount = (takerSellAmount * makerOrder.sellAmount) / makerOrder.buyAmount;
_fillOrderETH(makerOrder, makerSignature, msg.sender, msg.sender, takerBuyAmount, fillAvailable);
_refundETH();
returntrue;
}
/// @notice Fills an order with an exact amount to buy, taking or returning ETH/// @param makerOrder Order that will be used to make this swap, buyToken or sellToken must be WETH/// @param makerSignature Signature for the order used/// @param takerBuyAmount amount send to the sender from the maker/// @param fillAvailable Should the maximum buyAmount possible be used/// @return returns true if successfullfunctionfillOrderExactOutputETH(
LibOrder.Order calldata makerOrder,
bytescalldata makerSignature,
uint takerBuyAmount,
bool fillAvailable
) publicpayablereturns (bool) {
_fillOrderETH(makerOrder, makerSignature, msg.sender, msg.sender, takerBuyAmount, fillAvailable);
_refundETH();
returntrue;
}
function_fillOrderETH(
LibOrder.Order calldata makerOrder,
bytescalldata makerSignature,
address taker,
address takerReciver,
uint takerBuyAmountAdjusted,
bool fillAvailable
) internalreturns (uint256) {
require(makerOrder.buyToken == WETH_ADDRESS || makerOrder.sellToken == WETH_ADDRESS, 'Either buy or sell token should be WETH');
if (makerOrder.buyToken == WETH_ADDRESS) {
return
_fillOrder(
makerOrder,
makerSignature,
taker,
takerReciver,
makerOrder.sellToken,
ETH_ADDRESS,
takerBuyAmountAdjusted,
fillAvailable
);
} else {
return
_fillOrder(
makerOrder,
makerSignature,
taker,
takerReciver,
ETH_ADDRESS,
makerOrder.buyToken,
takerBuyAmountAdjusted,
fillAvailable
);
}
}
/// @notice Fills an order with an exact amount to sell/// @param makerOrder Order that will be used to make this swap/// @param makerSignature Signature for the order used/// @param takerSellAmount amount send from the sender to the maker/// @return returns true if successfullfunctionfillOrderExactInput(
LibOrder.Order calldata makerOrder,
bytescalldata makerSignature,
uint takerSellAmount,
bool fillAvailable
) publicreturns (bool) {
uint takerBuyAmount = (takerSellAmount * makerOrder.sellAmount) / makerOrder.buyAmount;
_fillOrder(makerOrder, makerSignature, msg.sender, msg.sender, makerOrder.sellToken, makerOrder.buyToken, takerBuyAmount, fillAvailable);
returntrue;
}
/// @notice Fills an order with an exact amount to buy/// @param makerOrder Order that will be used to make this swap/// @param makerSignature Signature for the order used/// @param takerBuyAmount amount send to the sender from the maker/// @param fillAvailable Should the maximum buyAmount possible be used/// @return returns true if successfullfunctionfillOrderExactOutput(
LibOrder.Order calldata makerOrder,
bytescalldata makerSignature,
uint takerBuyAmount,
bool fillAvailable
) publicreturns (bool) {
_fillOrder(makerOrder, makerSignature, msg.sender, msg.sender, makerOrder.sellToken, makerOrder.buyToken, takerBuyAmount, fillAvailable);
returntrue;
}
function_fillOrder(
LibOrder.Order calldata makerOrder,
bytescalldata makerSignature,
address taker,
address takerReciver,
address sellToken,
address buyToken,
uint takerBuyAmountAdjusted,
bool fillAvailable
) internalreturns (uint256) {
require(takerReciver != ETH_ADDRESS, "Can't recive to zero address");
LibOrder.OrderInfo memory makerOrderInfo = getOpenOrder(makerOrder);
// Check if the order is valid. We dont want to revert if the user wants to fill whats available, worst case that is 0.
{
(bool isValidOrder, stringmemory errorMsgOrder) = _isValidOrder(makerOrderInfo, makerOrder, makerSignature);
if (!isValidOrder && fillAvailable) return0;
require(isValidOrder, errorMsgOrder);
}
// adjust taker amountuint256 takerSellAmount;
{
uint256 availableTakerSellSize = makerOrder.sellAmount - makerOrderInfo.orderSellFilledAmount;
if (fillAvailable && availableTakerSellSize < takerBuyAmountAdjusted) takerBuyAmountAdjusted = availableTakerSellSize;
takerSellAmount = (takerBuyAmountAdjusted * makerOrder.buyAmount) / makerOrder.sellAmount;
require(takerBuyAmountAdjusted <= availableTakerSellSize, 'amount exceeds available size');
}
// check the maker balance/allowance with the adjusted taker amount
{
(bool isValidMaker, stringmemory errorMsgMaker) = _isValidMaker(makerOrder.user, sellToken, takerBuyAmountAdjusted);
if (!isValidMaker && fillAvailable) return0;
require(isValidMaker, errorMsgMaker);
}
// mark fills in storage
_updateOrderStatus(makerOrderInfo, makerOrder.sellAmount, takerBuyAmountAdjusted);
_settleMatchedOrders(makerOrder.user, taker, takerReciver, sellToken, buyToken, takerBuyAmountAdjusted, takerSellAmount);
return takerBuyAmountAdjusted;
}
function_settleMatchedOrders(address maker,
address taker,
address takerReciver,
address makerSellToken,
address takerSellToken,
uint makerSellAmount,
uint takerSellAmount
) internal{
if (takerSellToken == ETH_ADDRESS) {
require(msg.value>= takerSellAmount, 'msg value not high enough');
} elseif (taker != EXCHANGE_ADDRESS) {
require(IERC20(takerSellToken).balanceOf(taker) >= takerSellAmount, 'taker order not enough balance');
require(IERC20(takerSellToken).allowance(taker, EXCHANGE_ADDRESS) >= takerSellAmount, 'taker order not enough allowance');
}
// taker -> makerif (takerSellToken == ETH_ADDRESS) {
IWETH9(WETH_ADDRESS).depositTo{ value: takerSellAmount }(maker);
} elseif (taker == EXCHANGE_ADDRESS) {
IERC20(takerSellToken).transfer(maker, takerSellAmount);
} else {
IERC20(takerSellToken).transferFrom(taker, maker, takerSellAmount);
}
// maker -> takerif (makerSellToken == ETH_ADDRESS) {
IERC20(WETH_ADDRESS).transferFrom(maker, EXCHANGE_ADDRESS, makerSellAmount);
IWETH9(WETH_ADDRESS).withdrawTo(takerReciver, makerSellAmount);
} else {
IERC20(makerSellToken).transferFrom(maker, takerReciver, makerSellAmount);
}
emit Swap(maker, taker, makerSellToken, takerSellToken, makerSellAmount, takerSellAmount);
}
functiongetOpenOrder(LibOrder.Order calldata order) publicviewreturns (LibOrder.OrderInfo memory orderInfo) {
orderInfo.orderHash = getOrderHash(order);
orderInfo.orderSellFilledAmount = filled[orderInfo.orderHash];
}
functiongetOrderHash(LibOrder.Order calldata order) publicviewreturns (bytes32 orderHash) {
bytes32 contentHash = LibOrder.getContentHash(order);
orderHash = _hashTypedDataV4(contentHash);
}
functionisValidOrderSignature(LibOrder.Order calldata order, bytescalldata signature) publicviewreturns (bool) {
bytes32 orderHash = getOrderHash(order);
return _isValidSignatureHash(order.user, orderHash, signature);
}
function_isValidSignatureHash(address user, bytes32 hash, bytescalldata signature) privateviewreturns (bool) {
return SignatureChecker.isValidSignatureNow(user, hash, signature);
}
// always refund the one sending the msg, metaTx or nativeTxfunction_refundETH() internal{
if (address(this).balance>0) {
(bool success, ) =msg.sender.call{ value: address(this).balance }(newbytes(0));
require(success, 'ETH transfer failed');
}
}
function_isValidOrder(
LibOrder.OrderInfo memory orderInfo,
LibOrder.Order calldata order,
bytescalldata signature
) internalviewreturns (bool, stringmemory) {
if (!_isValidSignatureHash(order.user, orderInfo.orderHash, signature)) return (false, 'invalid maker signature');
if (cancelled[orderInfo.orderHash]) return (false, 'order canceled');
if (block.timestamp> order.expirationTimeSeconds) return (false, 'order expired');
if (order.sellAmount - orderInfo.orderSellFilledAmount ==0) return (false, 'order is filled');
return (true, '');
}
function_isValidMaker(address maker, address makerSellToken, uint256 takerAmount) internalviewreturns (bool, stringmemory) {
if (makerSellToken == ETH_ADDRESS) makerSellToken = WETH_ADDRESS;
uint256 balance = IERC20(makerSellToken).balanceOf(maker);
uint256 allowance = IERC20(makerSellToken).allowance(maker, EXCHANGE_ADDRESS);
if (balance < takerAmount) return (false, 'maker order not enough balance');
if (allowance < takerAmount) return (false, 'maker order not enough allowance');
return (true, '');
}
function_updateOrderStatus(LibOrder.OrderInfo memory makerOrderInfo, uint256 makerSellAmount, uint256 takerBuyAmount) internal{
uint makerOrderFilled = makerOrderInfo.orderSellFilledAmount + takerBuyAmount;
filled[makerOrderInfo.orderHash] = makerOrderFilled;
emit OrderStatus(makerOrderInfo.orderHash, makerOrderFilled, makerSellAmount - makerOrderFilled);
}
}