Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ contract Deploy is Script, Parameters {
tokenImplementation = address(new TMToken(factoryProxyAddress));
marketImplementation =
address(new TMMarket(factoryProxyAddress, quoteToken, amount0A, amount0B, sqrtPrice0, sqrtPrice1));
factoryImplementation = address(new TMFactory(0, 0, 0, 0, address(0), address(0), address(0), address(0)));
factoryImplementation =
address(new TMFactory(0, 0, 0, 0, address(0), address(0), address(0), address(0), wnative));

factoryProxy = address(
new TransparentUpgradeableProxy(
Expand Down
1 change: 1 addition & 0 deletions script/Parameters.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.20;

contract Parameters {
address internal quoteToken = 0x0000000000000000000000000000000000000000;
address internal wnative = 0x0000000000000000000000000000000000000000;
uint256 internal defaultProtocolFeeShare = 0.25e6; // 25%
uint256 internal defaultMinUpdateTime = 1 hours;
uint256 internal defaultFeeA = 0.02e6; // 2%
Expand Down
60 changes: 54 additions & 6 deletions src/TMFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet
import {ITMFactory} from "./interfaces/ITMFactory.sol";
import {ITMMarket} from "./interfaces/ITMMarket.sol";
import {ITMToken} from "./interfaces/ITMToken.sol";
import {IWNative} from "./interfaces/IWNative.sol";
import {Math} from "./libraries/Math.sol";
import {SwapMath} from "./libraries/SwapMath.sol";

Expand Down Expand Up @@ -37,6 +38,8 @@ contract TMFactory is AccessControlUpgradeable, ITMFactory {

bytes32 public constant override MARKET_MIGRATOR_ROLE = keccak256("MARKET_MIGRATOR_ROLE");

address public immutable override WNATIVE;

mapping(address quoteToken => address marketImplementation) private _marketImplementations;
address private _tokenImplementation;

Expand Down Expand Up @@ -66,8 +69,11 @@ contract TMFactory is AccessControlUpgradeable, ITMFactory {
address quoteToken,
address marketImplementation,
address tokenImplementation,
address admin
address admin,
address wnative
) {
WNATIVE = wnative;

initialize(
minUpdateTime,
protocolFeeShare,
Expand Down Expand Up @@ -228,18 +234,22 @@ contract TMFactory is AccessControlUpgradeable, ITMFactory {
/**
* @dev Creates a new market with {name}, {symbol}, {quoteToken} and {feeRecipient}.
* The market will be created with the default fee and the creator will be set to the caller of this function.
* The function also performs an initial swap by sending {amountQuoteIn} of {quoteToken} to the market
* Emits a {MarketCreated} event with the creator, quote token, market, token, name and symbol.
* Emits a {MarketDetailsUpdated} event with the market, creator and fee recipient.
*
* Requirements:
*
* - The {quoteToken} must be supported by the factory.
*/
function createMarket(string calldata name, string calldata symbol, address quoteToken, address feeRecipient)
external
override
returns (address token, address market)
{
function createMarket(
string calldata name,
string calldata symbol,
address quoteToken,
address feeRecipient,
uint256 amountQuoteIn,
uint256 minAmountBaseOut
) external payable override returns (address token, address market, uint256 amountBaseOut) {
if (feeRecipient != KOTM_FEE_RECIPIENT()) revert InvalidFeeRecipient();

(token, market) = _createMarket(name, symbol, quoteToken);
Expand All @@ -258,6 +268,34 @@ contract TMFactory is AccessControlUpgradeable, ITMFactory {

emit MarketCreated(msg.sender, quoteToken, market, token, name, symbol);
emit MarketDetailsUpdated(market, msg.sender, feeRecipient, address(0));

if (amountQuoteIn > 0) {
if (msg.value >= amountQuoteIn && quoteToken == WNATIVE) {
IWNative(quoteToken).deposit{value: amountQuoteIn}();
IERC20(quoteToken).safeTransfer(market, amountQuoteIn);
} else {
IERC20(quoteToken).safeTransferFrom(msg.sender, market, amountQuoteIn);
}

// forge-lint: disable-next-item(unsafe-typecast)
(int256 deltaBaseAmount, int256 deltaQuoteAmount) =
ITMMarket(market).swap(msg.sender, false, int256(amountQuoteIn), 2 ** 127 - 1);

// forge-lint: disable-next-line(unsafe-typecast)
if (uint256(deltaQuoteAmount) != amountQuoteIn) revert TooManyQuoteTokenSent();

// forge-lint: disable-next-line(unsafe-typecast)
amountBaseOut = uint256(-deltaBaseAmount);

if (amountBaseOut < minAmountBaseOut) revert InsufficientOutputAmount();
}

if (msg.value > 0) {
uint256 leftOver = address(this).balance;
if (leftOver > 0) {
_transferNative(msg.sender, leftOver);
}
}
}

/**
Expand Down Expand Up @@ -607,4 +645,14 @@ contract TMFactory is AccessControlUpgradeable, ITMFactory {

emit TokenImplementationSet(msg.sender, tokenImplementation);
}

/**
* @dev Transfers `amount` of native tokens to `to`.
* @param to The account to transfer the native tokens to.
* @param amount The amount of native tokens to transfer.
*/
function _transferNative(address to, uint256 amount) internal {
(bool success,) = to.call{value: amount}(new bytes(0));
if (!success) revert TransferFailed();
}
}
16 changes: 13 additions & 3 deletions src/interfaces/ITMFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ interface ITMFactory {
error InvalidMinUpdateTime();
error MismatchedQuoteToken();
error InvalidFeeRecipient();
error TooManyQuoteTokenSent();
error InsufficientOutputAmount();
error TransferFailed();

event MarketCreated(
address indexed creator, address indexed quoteToken, address market, address token, string name, string symbol
Expand Down Expand Up @@ -62,6 +65,8 @@ interface ITMFactory {

function MARKET_MIGRATOR_ROLE() external view returns (bytes32);

function WNATIVE() external view returns (address);

function getMarketImplementation(address quoteToken) external view returns (address);

function getTokenImplementation() external view returns (address);
Expand Down Expand Up @@ -94,9 +99,14 @@ interface ITMFactory {

function getMarketByCreatorAt(address creator, uint256 index) external view returns (address);

function createMarket(string calldata name, string calldata symbol, address quoteToken, address feeRecipient)
external
returns (address token, address market);
function createMarket(
string calldata name,
string calldata symbol,
address quoteToken,
address feeRecipient,
uint256 amountQuoteIn,
uint256 minAmountBaseOut
) external payable returns (address token, address market, uint256 amountBaseOut);

function migrateMarket(address market, address recipient) external returns (uint256 amount0, uint256 amount1);

Expand Down
14 changes: 14 additions & 0 deletions src/interfaces/IWNative.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
* @title Wrapped Native Interface
* @dev Interface of the wrapped native token.
*/
interface IWNative is IERC20 {
function deposit() external payable;

function withdraw(uint256 amount) external;
}
Loading