From 80482b38c1858abb709459967370b915262de838 Mon Sep 17 00:00:00 2001 From: Gary Ghayrat <61768337+garyghayrat@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:58:05 -0400 Subject: [PATCH] Add natspec (#46) * Add natspec comments Add comments to return values * Add additional comments * Clarify comments * Add a missing word * Add comments regarding reverts * Update comments in `CreatorTokenSwapRouter` * Fix small nit * Improve wording --- src/CreatorToken.sol | 173 ++++++++++++++++++++++++++++++++- src/CreatorTokenSwapRouter.sol | 82 ++++++++++++++-- 2 files changed, 248 insertions(+), 7 deletions(-) diff --git a/src/CreatorToken.sol b/src/CreatorToken.sol index 2b120f9..bca2d8b 100644 --- a/src/CreatorToken.sol +++ b/src/CreatorToken.sol @@ -7,6 +7,19 @@ import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol"; import {IBondingCurve} from "src/interfaces/IBondingCurve.sol"; +/// @title CreatorToken +/// @notice A token contract where creators can mint and sell tokens according to a price determined +/// by a bonding curve. +/// Key Features: +/// - Royalty System: Supports the ERC-2981 royalty standard, allowing creators to receive a +/// percentage of secondary sales. +/// - Dynamic Pricing: Prices for buying or selling tokens are determined by an integrated +/// bonding curve. +/// - Referral System: Has a built-in referral mechanism to mint a token for the referrer at +/// deployment. +/// - Fee Mechanism: Defines separate fees for both the creator and an admin, which are taken +/// from primary and secondary sales. +/// @dev This contract supports ERC-2981 royalty standard. contract CreatorToken is ERC721Royalty { using SafeERC20 for IERC20; @@ -20,24 +33,47 @@ contract CreatorToken is ERC721Royalty { error CreatorToken__MinHoldingTimeNotReached(uint256 holdingTime, uint256 minHoldingTime); error CreatorToken__ContractIsPaused(); + /// @notice ID of the last token minted. uint256 public lastId; + /// @notice Total supply of the creator tokens. uint256 public totalSupply; + /// @notice Address of the creator of the creator token. address public creator; + /// @notice Address of the admin of the creator token. address public admin; + /// @notice Address of the referrer, if any. address public immutable REFERRER; + /// @notice State indicating whether the contract is paused. bool public isPaused; + /// @notice URI pointing to the metadata for the creator token. string private creatorTokenURI; + /// @notice ERC20 token used for payments in the contract. IERC20 public payToken; + /// @notice Bonding curve contract used to determine token prices. IBondingCurve public immutable BONDING_CURVE; + /// @notice Constant to represent percentages in basis points. uint256 constant BIP = 10_000; + /// @notice Creator fee taken as a percentage when tokens are bought, in basis points. uint256 public immutable CREATOR_FEE_BIPS; + /// @notice Royalty fee for the creator, in basis points. uint96 public immutable CREATOR_ROYALTY_BIPS; + /// @notice Admin fee taken for the admin as a percentage, in basis points. uint256 public immutable ADMIN_FEE_BIPS; + /// @notice Maximum allowed fee in basis points. uint256 private constant MAX_FEE = 2500; // 25% in bips + /// @notice Minimum time a user must hold a token before selling, in blocks. uint256 private constant MIN_HOLDING_TIME = 60; + /// @notice Mapping to track purchase time of tokens. mapping(uint256 tokenId => uint256 blockTimestamp) internal purchaseTime; + /// @notice Event emitted when a new token is bought. + /// @param payer Address that paid for the token. + /// @param receiver Address that received the token. + /// @param tokenId ID of the token. + /// @param paymentAmount Amount of USDC paid for the token excluding fees. + /// @param creatorFee Amount of USDC paid to the creator. + /// @param adminFee Amount of USDC paid to the admin. event Bought( address indexed payer, address indexed receiver, @@ -46,6 +82,13 @@ contract CreatorToken is ERC721Royalty { uint256 creatorFee, uint256 adminFee ); + + /// @notice Event emitted when a token is sold. + /// @param seller Address that sold the token. + /// @param tokenId ID of the token. + /// @param salePrice Amount of USDC received for the token excluding fees. + /// @param creatorFee Amount of USDC paid to the creator. + /// @param adminFee Amount of USDC paid to the admin. event Sold( address indexed seller, uint256 indexed tokenId, @@ -53,16 +96,26 @@ contract CreatorToken is ERC721Royalty { uint256 creatorFee, uint256 adminFee ); + + /// @notice Event emitted when the contract pause state is toggled. event ToggledPause(bool oldPauseState, bool newPauseState, address caller); + + /// @notice Event emitted when the creator is updated. event CreatorUpdated(address oldCreator, address newCreator); + + /// @notice Event emitted when the admin is updated. event AdminUpdated(address oldAdmin, address newAdmin); + + /// @notice Event emitted when the tokenURI is updated. event TokenURIUpdated(string oldTokenURI, string newTokenURI); + /// @notice Ensures that the given address is not the zero address. modifier isNotAddressZero(address _address) { if (_address == address(0)) revert CreatorToken__AddressZeroNotAllowed(); _; } + /// @notice Ensures that the caller is either the creator or the admin. modifier onlyCreatorOrAdmin(address _caller) { if (_caller != creator && _caller != admin) { revert CreatorToken__Unauthorized("not creator or admin", _caller); @@ -70,11 +123,25 @@ contract CreatorToken is ERC721Royalty { _; } + /// @notice Ensures that the contract is not in a paused state. modifier whenNotPaused() { if (isPaused) revert CreatorToken__ContractIsPaused(); _; } + /// @notice Initializes a new CreatorToken contract. + /// @dev Sets initial values for URI, fees, and initializes the ERC721 contract. + /// @param _name The name of the ERC721 token. + /// @param _symbol The symbol of the ERC721 token. + /// @param _tokenURI The URI for the creator token. + /// @param _creator Address of the creator. + /// @param _creatorFee Creator fee in BIPs. + /// @param _creatorRoyalty Creator royalty fee in BIPs. + /// @param _admin Address of the admin. + /// @param _adminFee Admin fee in BIPs. + /// @param _referrer Address of the referrer. + /// @param _payToken ERC20 token used for payments. + /// @param _bondingCurve The bonding curve contract for token pricing. constructor( string memory _name, string memory _symbol, @@ -107,10 +174,19 @@ contract CreatorToken is ERC721Royalty { if (_referrer != address(0)) _mintAndIncrement(_referrer); } + /// @notice Purchase a token. + /// @dev Reverts if the total price exceeds `_maxPayment`. + /// @param _maxPayment The maximum amount of USDC the buyer is willing to pay. + /// @return _totalPrice The total amount of USDC paid for the creator token. function buy(uint256 _maxPayment) public returns (uint256 _totalPrice) { _totalPrice = buy(msg.sender, _maxPayment); } + /// @notice Purchase a token and mint to another address. + /// @dev Reverts if the total price exceeds `_maxPayment`. + /// @param _to Address to receive the token. + /// @param _maxPayment The maximum amount of USDC the buyer is willing to pay. + /// @return _totalPrice The total amount of USDC paid for the creator token. function buy(address _to, uint256 _maxPayment) public returns (uint256 _totalPrice) { (uint256 _tokenPrice, uint256 _creatorFee, uint256 _adminFee) = _buyWithoutPayment(_to); _totalPrice = _tokenPrice + _creatorFee + _adminFee; @@ -124,10 +200,22 @@ contract CreatorToken is ERC721Royalty { payToken.safeTransferFrom(msg.sender, admin, _adminFee); } + /// @notice Buy multiple creator tokens in bulk and mint them to the caller. + /// @dev The recipient of the creator tokens is the msg.sender. + /// @dev Reverts if the total price exceeds `_maxPayment`. + /// @param _numOfTokens Number of tokens to buy. + /// @param _maxPayment The maximum amount of USDC the caller is willing to pay. + /// @return _totalPrice The total amount of USDC paid for the creator tokens including fees. function bulkBuy(uint256 _numOfTokens, uint256 _maxPayment) public returns (uint256 _totalPrice) { _totalPrice = bulkBuy(msg.sender, _numOfTokens, _maxPayment); } + /// @notice Buy multiple tokens in bulk and mint them to a specified address. + /// @dev Reverts if the total price exceeds `_maxPayment`. + /// @param _to Address where the tokens should be sent. + /// @param _numOfTokens Number of tokens to buy. + /// @param _maxPayment The maximum amount of USDC the caller is willing to pay. + /// @return _totalPrice The total amount of USDC paid for the creator tokens including fees. function bulkBuy(address _to, uint256 _numOfTokens, uint256 _maxPayment) public returns (uint256 _totalPrice) @@ -160,10 +248,22 @@ contract CreatorToken is ERC721Royalty { payToken.safeTransferFrom(msg.sender, admin, _totalAdminFee); } + /// @notice Sell a token. + /// @dev Sets the minimum accepted proceeds to 0. + /// @dev Reverts if the token is sold before the minimum holding time. + /// @dev Reverts if the net proceeds from the sale are less than `_minAcceptedPrice`. + /// @param _tokenId ID of the token to be sold. + /// @return _netProceeds The net proceeds from the sale in USDC after fees. function sell(uint256 _tokenId) public returns (uint256 _netProceeds) { - _netProceeds = sell(_tokenId, 0); // TODO: consider how to test this is curried correctly + _netProceeds = sell(_tokenId, 0); } + /// @notice Sell a token with a minimum accepted price. + /// @dev Reverts if the token is sold before the minimum holding time. + /// @dev Reverts if the net proceeds from the sale are less than `_minAcceptedPrice`. + /// @param _tokenId ID of the token to be sold. + /// @param _minAcceptedPrice The minimum proceed in USDC the seller is willing to accept. + /// @return _netProceeds The net proceeds in USDC from the sale after fees. function sell(uint256 _tokenId, uint256 _minAcceptedPrice) public returns (uint256 _netProceeds) { uint256 _creatorFee; uint256 _adminFee; @@ -179,10 +279,23 @@ contract CreatorToken is ERC721Royalty { payToken.safeTransfer(admin, _adminFee); } + /// @notice Sell multiple tokens in bulk. + /// @dev Sets the minimum accepted proceeds to 0. + /// @dev Reverts if any of the tokens are sold before the minimum holding time. + /// @dev Reverts if the net proceeds from the sale are less than `_minAcceptedPrice`. + /// @param _tokenIds Array of token IDs to be sold. + /// @return _netProceeds The total net proceeds in USDC from the bulk sale. function bulkSell(uint256[] memory _tokenIds) public returns (uint256 _netProceeds) { _netProceeds = bulkSell(_tokenIds, 0); } + /// @notice Sell multiple tokens with a minimum accepted proceeds in USDC for the total sale. + /// @dev Reverts if any of the tokens are sold before the minimum holding time. + /// @dev Reverts if the net proceeds from the sale are less than `_minAcceptedPrice`. + /// @param _tokenIds Array of token IDs to be sold. + /// @param _minAcceptedPrice The minimum total net proceeds in USDC the seller is willing to + /// accept for the bulk sale. + /// @return _netProceeds The total net proceeds from the bulk sale. function bulkSell(uint256[] memory _tokenIds, uint256 _minAcceptedPrice) public returns (uint256 _netProceeds) @@ -213,6 +326,9 @@ contract CreatorToken is ERC721Royalty { payToken.safeTransfer(admin, _totalAdminFee); } + /// @notice Updates the creator of the contract. + /// @dev Only the current creator can update the creator address. + /// @param _newCreator Address of the new creator. function updateCreator(address _newCreator) public isNotAddressZero(_newCreator) { if (msg.sender != creator) revert CreatorToken__Unauthorized("not creator", msg.sender); creator = _newCreator; @@ -220,21 +336,36 @@ contract CreatorToken is ERC721Royalty { emit CreatorUpdated(msg.sender, _newCreator); } + /// @notice Updates the admin of the contract. + /// @dev Only the current admin can update the admin address. + /// @param _newAdmin Address of the new admin. function updateAdmin(address _newAdmin) public isNotAddressZero(_newAdmin) { if (msg.sender != admin) revert CreatorToken__Unauthorized("not admin", msg.sender); admin = _newAdmin; emit AdminUpdated(msg.sender, _newAdmin); } + /// @notice Retrieves the URI of the token. + /// @dev This function overrides the `tokenURI` function from ERC-721 standard. + /// @return URI of the token. function tokenURI(uint256) public view override returns (string memory) { return creatorTokenURI; } + /// @notice Updates the URI of the token. + /// @dev Only the creator or admin can update the token URI. + /// @param _newTokenURI New URI for the token. function updateTokenURI(string memory _newTokenURI) public onlyCreatorOrAdmin(msg.sender) { emit TokenURIUpdated(creatorTokenURI, _newTokenURI); creatorTokenURI = _newTokenURI; } + /// @notice Handles the internal minting logic for a token without payment. + /// @dev The function mints and transfers a token. + /// @param _to Address where the new creator token should be sent. + /// @return _tokenPrice The price of the token in USDC. + /// @return _creatorFee The creator's fee in USDC. + /// @return _adminFee The admin's fee in USDC. function _buyWithoutPayment(address _to) internal whenNotPaused @@ -247,6 +378,12 @@ contract CreatorToken is ERC721Royalty { emit Bought(msg.sender, _to, lastId, _tokenPrice, _creatorFee, _adminFee); } + /// @notice Handles the internal burn logic for a token without payment. + /// @dev The function transfers and burns a token. + /// @param _tokenId ID of the token to be sold. + /// @return _netProceeds The net proceeds from the sale after deducting fees. + /// @return _creatorFee The creator's fee in USDC. + /// @return _adminFee The admin's fee in USDC. function _sellWithoutPayment(uint256 _tokenId) internal whenNotPaused @@ -274,11 +411,18 @@ contract CreatorToken is ERC721Royalty { emit Sold(msg.sender, _tokenId, _tokenPrice, _creatorFee, _adminFee); } + /// @notice Set a new pause state for the contract. + /// @dev Only the creator or admin can pause/unpause the contract. + /// @param _pauseState The desired paused state: true to pause, false to unpause. function pause(bool _pauseState) public onlyCreatorOrAdmin(msg.sender) { emit ToggledPause(isPaused, _pauseState, msg.sender); isPaused = _pauseState; } + /// @notice Retrieves the price for the next token to be bought in USDC. + /// @return _tokenPrice The price for the next token in USDC. + /// @return _creatorFee The creator's fee for the next token in USDC. + /// @return _adminFee The admin's fee for the next token in USDC. function priceToBuyNext() public view @@ -288,6 +432,11 @@ contract CreatorToken is ERC721Royalty { (_creatorFee, _adminFee) = calculateFees(_tokenPrice); } + /// @notice Calculates the aggregated price for the next N tokens to be bought in USDC. + /// @param _numOfTokens The number of tokens to calculate the price for. + /// @return _tokenPrice The aggregated price for the next N tokens in USDC. + /// @return _creatorFee The aggregated creator's fee for the next N tokens in USDC. + /// @return _adminFee The aggregated admin's fee for the next N tokens in USDC. function priceToBuyNext(uint256 _numOfTokens) public view @@ -316,6 +465,10 @@ contract CreatorToken is ERC721Royalty { } } + /// @notice Retrieves the selling price for the next token to be sold in USDC. + /// @return _tokenPrice The selling price for the next token in USDC. + /// @return _creatorFee The creator's fee for the next token in USDC. + /// @return _adminFee The admin's fee for the next token in USDC. function priceToSellNext() public view @@ -325,6 +478,11 @@ contract CreatorToken is ERC721Royalty { (_creatorFee, _adminFee) = calculateFees(_tokenPrice); } + /// @notice Calculates the aggregated selling price for the next N tokens to be sold in USDC. + /// @param _numOfTokens The number of tokens to calculate the selling price for. + /// @return _tokenPrice The aggregated selling price for the next N tokens in USDC. + /// @return _creatorFee The aggregated creator's fee for the next N tokens in USDC. + /// @return _adminFee The aggregated admin's fee for the next N tokens in USDC. function priceToSellNext(uint256 _numOfTokens) public view @@ -346,6 +504,10 @@ contract CreatorToken is ERC721Royalty { } } + /// @notice Calculates the creator and admin fees in USDC based on a given price in USDC. + /// @param _price The base price in USDC to calculate fees from. + /// @return _creatorFee The fee owed to the creator in USDC. + /// @return _adminFee The fee owed to the admin in USDC. function calculateFees(uint256 _price) public view @@ -355,16 +517,25 @@ contract CreatorToken is ERC721Royalty { _adminFee = (_price * ADMIN_FEE_BIPS) / BIP; } + /// @notice Determines the pre-mint offset based on whether a referrer is set. + /// @dev If the REFERRER address is zero, the offset is 1; otherwise, it's 2. + /// @return _offset The determined pre-mint offset. function _preMintOffset() private view returns (uint256 _offset) { _offset = REFERRER == address(0) ? 1 : 2; } + /// @notice Mints a new token to the specified address and increments the lastId and totalSupply. + /// @dev Internal function to for minting logic and tracking. + /// @param _to Address to which the new token will be minted. function _mintAndIncrement(address _to) private { lastId += 1; _mint(_to, lastId); totalSupply += 1; } + /// @notice Burns the specified token and decrements the totalSupply counter. + /// @dev Internal function for burn logic and tracking. + /// @param _tokenId The ID of the token to be burned. function _burnAndDecrement(uint256 _tokenId) private { _burn(_tokenId); totalSupply -= 1; diff --git a/src/CreatorTokenSwapRouter.sol b/src/CreatorTokenSwapRouter.sol index 1247dc7..5551010 100644 --- a/src/CreatorTokenSwapRouter.sol +++ b/src/CreatorTokenSwapRouter.sol @@ -5,31 +5,52 @@ import {IUniversalRouter} from "src/lib/IUniversalRouter.sol"; import {ICreatorToken} from "src/interfaces/ICreatorToken.sol"; import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; +/// @title CreatorTokenSwapRouter +/// @notice A contract for swapping ETH to USDC and then buying Creator Tokens. +/// @dev Make sure to get a quote for ETH to Creator Token conversion before interacting. contract CreatorTokenSwapRouter { + /// @notice Uniswap UniversalRouter interface which is used to execute trades. IUniversalRouter private immutable UNIVERSAL_ROUTER; + /// @notice Address of the Wrapped Ether (WETH) token. address public immutable WETH_ADDRESS; + /// @notice Address of the USDC token. address public immutable USDC_ADDRESS; - // for WRAP_ETH, V3_SWAP_EXACT_OUT, and UNWRAP_WETH + /// @notice Command bytes used for specific operations such as WRAP_ETH(0x0b), + /// V3_SWAP_EXACT_OUT(0x01), and UNWRAP_WETH(0x0c). bytes private constant COMMANDS = abi.encodePacked(bytes1(uint8(0x0b)), bytes1(uint8(0x01)), bytes1(uint8(0x0c))); + /// @notice Low fee tier for Uniswap V3 WETH-USDC swaps. bytes3 private constant LOW_FEE_TIER = bytes3(uint24(500)); - // WETH -> USDC, order is switched because of V3_SWAP_EXACT_OUT + /// @notice Path for swapping WETH to USDC. bytes private path; + /// @notice Contract constructor sets up the Universal Router and token addresses. + /// @param _universalRouter Address of the Universal Router contract. + /// @param _wethAddress Address of the WETH token contract. + /// @param _usdcAddress Address of the USDC token contract. constructor(address _universalRouter, address _wethAddress, address _usdcAddress) { UNIVERSAL_ROUTER = IUniversalRouter(_universalRouter); WETH_ADDRESS = _wethAddress; USDC_ADDRESS = _usdcAddress; + // WETH -> USDC, order is switched because of V3_SWAP_EXACT_OUT path = bytes.concat(bytes20(address(USDC_ADDRESS)), LOW_FEE_TIER, bytes20(address(WETH_ADDRESS))); } - // You can get a quote for the amount of ETH you have to pay to buy a creator token by calling - // Uniswap's `QuoterV2` contract off-chain. - // Check Uniswap docs: https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and - // `quote` function in `test/SwapRouter.fork.8453.t.sol.sol` + /// @notice Buys a Creator Token with ETH from the caller. + /// @dev Transactions calling this function must include an ETH payment as its `value`. This ETH + /// is then swapped for the equivalent amount of USDC needed to purchase the Creator Token. + /// @dev Ensure that the ETH `value` sent with the transaction is equal to or greater than + /// the amount of USDC required for the purchase. + /// @dev You can get a quote for the amount of ETH you have to pay to get a certain amount of USDC + /// by calling Uniswap's `QuoterV2` contract off-chain. Check Uniswap docs: + /// https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and `quote` function + /// in `test/SwapRouter.fork.8453.t.sol.sol` + /// @param _creatorToken Address of the Creator Token contract. + /// @param _maxPayment Maximum amount of USDC willing to be paid for the token. + /// @return _amountOut The amount of USDC paid. function buyWithEth(address _creatorToken, uint256 _maxPayment) external payable @@ -38,6 +59,19 @@ contract CreatorTokenSwapRouter { _amountOut = buyWithEth(_creatorToken, msg.sender, _maxPayment); } + /// @notice Buys creator tokens with ETH and sends them to a specified address. + /// @dev Transactions calling this function must include an ETH payment as its `value`. This ETH + /// is then swapped for the equivalent amount of USDC needed to purchase the Creator Token. + /// @dev Ensure that the ETH `value` sent with the transaction is equal to or greater than + /// the amount of USDC required for the purchase. + /// @dev You can get a quote for the amount of ETH you have to pay to get a certain amount of USDC + /// by calling Uniswap's `QuoterV2` contract off-chain. Check Uniswap docs: + /// https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and `quote` function + /// in `test/SwapRouter.fork.8453.t.sol.sol` + /// @param _creatorToken The address of the creator token to buy. + /// @param _to The address to send the purchased tokens. + /// @param _maxPayment The maximum amount of USDC to be paid. + /// @return _amountOut The amount of USDC paid. function buyWithEth(address _creatorToken, address _to, uint256 _maxPayment) public payable @@ -48,6 +82,19 @@ contract CreatorTokenSwapRouter { _amountOut = ICreatorToken(_creatorToken).buy(_to, _maxPayment); } + /// @notice Buys a specified number of creator tokens with ETH and sends them to the caller. + /// @dev Transactions calling this function must include an ETH payment as its `value`. This ETH + /// is then swapped for the equivalent amount of USDC needed to purchase the Creator Tokens. + /// @dev Ensure that the ETH `value` sent with the transaction is equal to or greater than + /// the amount of USDC required for the purchase. + /// @dev You can get a quote for the amount of ETH you have to pay to get a certain amount of USDC + /// by calling Uniswap's `QuoterV2` contract off-chain. Check Uniswap docs: + /// https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and `quote` function + /// in `test/SwapRouter.fork.8453.t.sol.sol` + /// @param _creatorToken The address of the creator token to buy. + /// @param _numOfTokens The number of tokens to buy. + /// @param _maxPayment The maximum amount of USDC to be paid. + /// @return _amountOut The amount of USDC paid. function bulkBuyWithEth(address _creatorToken, uint256 _numOfTokens, uint256 _maxPayment) external payable @@ -56,6 +103,21 @@ contract CreatorTokenSwapRouter { _amountOut = bulkBuyWithEth(_creatorToken, msg.sender, _numOfTokens, _maxPayment); } + /// @notice Buys a specified number of creator tokens with ETH and sends them to a specified + /// address. + /// @dev Transactions calling this function must include an ETH payment as its `value`. This ETH + /// is then swapped for the equivalent amount of USDC needed to purchase the Creator Tokens. + /// @dev Ensure that the ETH `value` sent with the transaction is equal to or greater than + /// the amount of USDC required for the purchase. + /// @dev You can get a quote for the amount of ETH you have to pay to get a certain amount of USDC + /// by calling Uniswap's `QuoterV2` contract off-chain. Check Uniswap docs: + /// https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and `quote` function + /// in `test/SwapRouter.fork.8453.t.sol.sol` + /// @param _creatorToken The address of the creator token to buy. + /// @param _to The address to send the purchased creator tokens. + /// @param _numOfTokens The number of tokens to buy. + /// @param _maxPayment The maximum amount of USDC to be paid. + /// @return _amountOut The amount of USDC paid. function bulkBuyWithEth( address _creatorToken, address _to, @@ -67,11 +129,16 @@ contract CreatorTokenSwapRouter { _amountOut = ICreatorToken(_creatorToken).bulkBuy(_to, _numOfTokens, _maxPayment); } + /// @notice Swaps ETH for USDC. + /// @param _creatorToken The address of the creator token. + /// @param _numOfTokens The number of tokens to swap. + /// @param _amountIn The amount of ETH to swap. function _swapEthForUSDC(address _creatorToken, uint256 _numOfTokens, uint256 _amountIn) private { // // Encoding the inputs for V3_SWAP_EXACT_IN bytes[] memory inputs = new bytes[](3); (uint256 _tokenPrice, uint256 _creatorFee, uint256 _adminFee) = ICreatorToken(_creatorToken).priceToBuyNext(_numOfTokens); + // Amount of USDC required to purchase `_numOfTokens` uint256 _amountOut = _tokenPrice + _creatorFee + _adminFee; // WRAP_ETH inputs[0] = abi.encode(address(UNIVERSAL_ROUTER), _amountIn); @@ -84,6 +151,9 @@ contract CreatorTokenSwapRouter { UNIVERSAL_ROUTER.execute{value: _amountIn}(COMMANDS, inputs, block.timestamp + 60); } + /// @dev Approves the creator token contract to transfer USDC from this contract. + /// @param _creatorToken The address of the creator token. + /// @param _maxPayment The amount of USDC to be spent. function _approveCreatorToken(address _creatorToken, uint256 _maxPayment) private { if (IERC20(USDC_ADDRESS).allowance(address(this), address(_creatorToken)) < _maxPayment) { IERC20(USDC_ADDRESS).approve(address(_creatorToken), type(uint256).max);