diff --git a/contracts/BookManager.sol b/contracts/BookManager.sol new file mode 100644 index 0000000..b7fef73 --- /dev/null +++ b/contracts/BookManager.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract BookManager { + constructor() {} +} diff --git a/contracts/interfaces/IBookManager.sol b/contracts/interfaces/IBookManager.sol new file mode 100644 index 0000000..732baf0 --- /dev/null +++ b/contracts/interfaces/IBookManager.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import "../libraries/CurrencyLibrary.sol"; + +interface IBookManager { + struct BookKey { + Currency base; + uint8 baseUnitDecimals; + Currency quote; + uint8 quoteUnitDecimals; + uint24 makerFee; + uint24 takerFee; + uint24 tickSpacing; + } +} diff --git a/contracts/libraries/BookId.sol b/contracts/libraries/BookId.sol new file mode 100644 index 0000000..bab3bad --- /dev/null +++ b/contracts/libraries/BookId.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract BookId { + constructor() {} +} diff --git a/contracts/libraries/CurrencyLibrary.sol b/contracts/libraries/CurrencyLibrary.sol new file mode 100644 index 0000000..9a034c0 --- /dev/null +++ b/contracts/libraries/CurrencyLibrary.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.20; + +import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol"; + +type Currency is address; + +/// @title CurrencyLibrary +/// @dev This library allows for transferring and holding native tokens and ERC20 tokens +library CurrencyLibrary { + using CurrencyLibrary for Currency; + + /// @notice Thrown when a native transfer fails + error NativeTransferFailed(); + + /// @notice Thrown when an ERC20 transfer fails + error ERC20TransferFailed(); + + Currency public constant NATIVE = Currency.wrap(address(0)); + + function transfer( + Currency currency, + address to, + uint256 amount + ) internal { + // implementation from + // https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/SafeTransferLib.sol + + bool success; + if (currency.isNative()) { + assembly { + // Transfer the ETH and store if it succeeded or not. + success := call(gas(), to, amount, 0, 0, 0, 0) + } + + if (!success) revert NativeTransferFailed(); + } else { + assembly { + // We'll write our calldata to this slot below, but restore it later. + let memPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(0, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) + mstore(4, to) // Append the "to" argument. + mstore(36, amount) // Append the "amount" argument. + + success := and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 68 because that's the total length of our calldata (4 + 32 * 2) + // Counterintuitively, this call() must be positioned after the or() in the + // surrounding and() because and() evaluates its arguments from right to left. + call(gas(), currency, 0, 0, 68, 0, 32) + ) + + mstore(0x60, 0) // Restore the zero slot to zero. + mstore(0x40, memPointer) // Restore the memPointer. + } + + if (!success) revert ERC20TransferFailed(); + } + } + + function balanceOfSelf(Currency currency) internal view returns (uint256) { + if (currency.isNative()) { + return address(this).balance; + } else { + return IERC20Minimal(Currency.unwrap(currency)).balanceOf(address(this)); + } + } + + function equals(Currency currency, Currency other) internal pure returns (bool) { + return Currency.unwrap(currency) == Currency.unwrap(other); + } + + function isNative(Currency currency) internal pure returns (bool) { + return Currency.unwrap(currency) == Currency.unwrap(NATIVE); + } + + function toId(Currency currency) internal pure returns (uint256) { + return uint160(Currency.unwrap(currency)); + } + + function fromId(uint256 id) internal pure returns (Currency) { + return Currency.wrap(address(uint160(id))); + } +}