diff --git a/script/DeployTokenFactory.s.sol b/script/DeployTokenFactory.s.sol index baf97b6..9a3ad9a 100644 --- a/script/DeployTokenFactory.s.sol +++ b/script/DeployTokenFactory.s.sol @@ -71,7 +71,7 @@ contract Deploy is Script { // @dev deploy spotlight token factory proxy contract TransparentUpgradeableProxy factoryProxy = new TransparentUpgradeableProxy(address(factoryImpl), _SPOTLIGHT_TOKEN_FACTORY_OWNER, ""); - SpotlightTokenFactory(address(factoryProxy)).initialize( + SpotlightTokenFactory(payable(address(factoryProxy))).initialize( _SPOTLIGHT_TOKEN_FACTORY_OWNER, // owner_ 0.1 ether, // creationFee: 0.1 ether address(tokenIpCollection), // tokenIpCollection_ diff --git a/src/spotlight-protocol-rewards/ISpotlightProtocolRewards.sol b/src/spotlight-protocol-rewards/ISpotlightProtocolRewards.sol index a9827ca..2952856 100644 --- a/src/spotlight-protocol-rewards/ISpotlightProtocolRewards.sol +++ b/src/spotlight-protocol-rewards/ISpotlightProtocolRewards.sol @@ -40,37 +40,37 @@ interface ISpotlightProtocolRewards { * - The caller provides ETH value to deposit as rewards. * - Emits a Deposit event. * - * @param ipaId The IPA identifier address to deposit rewards for. + * @param ipAccount The IPAccount address to deposit rewards for. */ - function deposit(address ipaId) external payable; + function deposit(address ipAccount) external payable; /** * @dev Withdraws rewards for a specific IPA. - * - Only the IPA owner can withdraw. + * - Only the IPAccount owner can withdraw. * - Requires withdraw functionality to be enabled. * - Emits a Withdraw event. * - * @param ipaId The IPA identifier address to withdraw rewards from. + * @param ipAccount The IPAccount address to withdraw rewards from. */ - function withdraw(address ipaId) external; + function withdraw(address ipAccount) external; /** * @dev Withdraws rewards for multiple IPAs at once. - * - Only IPA owners can withdraw their respective rewards. + * - Only IPAccount owners can withdraw their respective rewards. * - Requires withdraw functionality to be enabled. * - Emits a Withdraw event with total amount. * - * @param ipaIds Array of IPA identifier addresses to withdraw rewards from. + * @param ipAccounts Array of IPAccount addresses to withdraw rewards from. */ - function withdrawAll(address[] calldata ipaIds) external; + function withdrawAll(address[] calldata ipAccounts) external; /** - * @dev Returns the pending rewards for an IPA. + * @dev Returns the pending rewards for an IPAccount. * - * @param ipaId The IPA identifier address to check rewards for. + * @param ipAccount The IPAccount address to check rewards for. * @return The amount of pending rewards. */ - function rewardsOf(address ipaId) external view returns (uint256); + function rewardsOf(address ipAccount) external view returns (uint256); /** * @dev Returns the total rewards held in the contract. diff --git a/src/spotlight-protocol-rewards/SpotlightProtocolRewards.sol b/src/spotlight-protocol-rewards/SpotlightProtocolRewards.sol index 3699d8b..3bdd0e8 100644 --- a/src/spotlight-protocol-rewards/SpotlightProtocolRewards.sol +++ b/src/spotlight-protocol-rewards/SpotlightProtocolRewards.sol @@ -22,7 +22,7 @@ contract SpotlightProtocolRewards is ISpotlightProtocolRewards, Ownable { error TransferFailed(); error TokenCallFailed(); error WithdrawDisabled(); - error IpaIdsEmpty(); + error IPAccountsEmpty(); modifier onlyWithdrawEnabled() { if (!_withdrawEnabled) revert WithdrawDisabled(); @@ -41,14 +41,14 @@ contract SpotlightProtocolRewards is ISpotlightProtocolRewards, Ownable { /* * @dev Deposit protocol rewards * - * @param ipaId The IpaId to deposit rewards for + * @param ipAccount The IPAccount to deposit rewards for */ - function deposit(address ipaId) external payable { - if (ipaId == address(0)) revert AddressZero(); + function deposit(address ipAccount) external payable { + if (ipAccount == address(0)) revert AddressZero(); uint256 amount = msg.value; if (amount == 0) revert AmountZero(); - (, address tokenContract, uint256 tokenId) = _getToken(ipaId); + (, address tokenContract, uint256 tokenId) = _getToken(ipAccount); _rewards[tokenContract][tokenId] += amount; emit Deposit(tokenContract, tokenId, amount); @@ -57,12 +57,12 @@ contract SpotlightProtocolRewards is ISpotlightProtocolRewards, Ownable { /* * @dev Withdraw protocol rewards * - * @param ipaId The IpaId to withdraw rewards from + * @param ipAccount The IPAccount to withdraw rewards from */ - function withdraw(address ipaId) external onlyWithdrawEnabled { - if (ipaId == address(0)) revert AddressZero(); + function withdraw(address ipAccount) external onlyWithdrawEnabled { + if (ipAccount == address(0)) revert AddressZero(); - (, address tokenContract, uint256 tokenId) = _getToken(ipaId); + (, address tokenContract, uint256 tokenId) = _getToken(ipAccount); if (!_checkIsOwner(tokenContract, tokenId)) revert InvalidWithdraw(); uint256 ownerRewards = this.rewardsOf(tokenContract, tokenId); @@ -79,19 +79,19 @@ contract SpotlightProtocolRewards is ISpotlightProtocolRewards, Ownable { /* * @dev Withdraw protocol rewards * - * @param ipaIds Array of IpaIds to withdraw rewards from + * @param ipAccounts Array of IPAccounts to withdraw rewards from */ - function withdrawAll(address[] calldata ipaIds) external onlyWithdrawEnabled { - if (ipaIds.length == 0) revert IpaIdsEmpty(); + function withdrawAll(address[] calldata ipAccounts) external onlyWithdrawEnabled { + if (ipAccounts.length == 0) revert IPAccountsEmpty(); uint256 totalAmount; - WithdrawInfo[] memory withdrawals = new WithdrawInfo[](ipaIds.length); + WithdrawInfo[] memory withdrawals = new WithdrawInfo[](ipAccounts.length); - for (uint256 i = 0; i < ipaIds.length; i++) { - address ipaId = ipaIds[i]; - if (ipaId == address(0)) revert AddressZero(); + for (uint256 i = 0; i < ipAccounts.length; i++) { + address ipAccount = ipAccounts[i]; + if (ipAccount == address(0)) revert AddressZero(); - (, address tokenContract, uint256 tokenId) = _getToken(ipaId); + (, address tokenContract, uint256 tokenId) = _getToken(ipAccount); if (!_checkIsOwner(tokenContract, tokenId)) { revert InvalidWithdraw(); } @@ -130,16 +130,16 @@ contract SpotlightProtocolRewards is ISpotlightProtocolRewards, Ownable { } /* - * @dev Get the specific rewards for an IpaId + * @dev Get the specific rewards for an IPAccount * - * @param ipaId The address to check the rewards for + * @param ipAccount The address to check the rewards for * - * @return uint256 The specific rewards for the IpaId + * @return uint256 The specific rewards for the IPAccount */ - function rewardsOf(address ipaId) external view returns (uint256) { - if (ipaId == address(0)) revert AddressZero(); + function rewardsOf(address ipAccount) external view returns (uint256) { + if (ipAccount == address(0)) revert AddressZero(); - (, address tokenContract, uint256 tokenId) = _getToken(ipaId); + (, address tokenContract, uint256 tokenId) = _getToken(ipAccount); return this.rewardsOf(tokenContract, tokenId); } @@ -159,14 +159,18 @@ contract SpotlightProtocolRewards is ISpotlightProtocolRewards, Ownable { /* * @dev Internal function to get token info using low level call - * @param ipaId The address of the contract to call + * @param ipAccount The address of the contract to call * * @return chainId The EIP-155 ID of the chain the token exists on * @return tokenContract The contract address of the token * @return tokenId The ID of the token */ - function _getToken(address ipaId) internal view returns (uint256 chainId, address tokenContract, uint256 tokenId) { - (chainId, tokenContract, tokenId) = IMinimalIPAccount(ipaId).token(); + function _getToken(address ipAccount) + internal + view + returns (uint256 chainId, address tokenContract, uint256 tokenId) + { + (chainId, tokenContract, tokenId) = IMinimalIPAccount(ipAccount).token(); if (tokenContract == address(0)) revert AddressZero(); } diff --git a/src/spotlight-token-factory/SpotlightTokenFactory.sol b/src/spotlight-token-factory/SpotlightTokenFactory.sol index 6ca293e..d0fa90e 100644 --- a/src/spotlight-token-factory/SpotlightTokenFactory.sol +++ b/src/spotlight-token-factory/SpotlightTokenFactory.sol @@ -222,6 +222,8 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF require(success, "SpotlightTokenFactory: Failed to claim fee"); } + receive() external payable {} + // @dev - private functions function _checkIsInitialized() internal view { if (!isInitialized()) { @@ -237,23 +239,21 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF return keccak256(abi.encodePacked(account, numberOfTokensCreated(account))); } - function _deploySpotlightToken(TokenCreationData memory tokenCreationData, address creator, address specificAddress) + function _deploySpotlightToken(TokenCreationData memory tokenCreationData, address creator, address ipAccount) internal returns (address) { address tokenAddress = Clones.cloneDeterministic(_tokenImplementation, _salt(creator)); ISpotlightToken(tokenAddress).initialize( - owner(), - creator, - _bondingCurve, - _baseToken, - owner(), tokenCreationData.tokenName, tokenCreationData.tokenSymbol, + creator, + _bondingCurve, + address(this), + ipAccount, + _protocolRewards, _piperXRouter, - _piperXFactory, - specificAddress, - _protocolRewards + _piperXFactory ); if (tokenAddress != tokenCreationData.predeployedTokenAddress) { revert("The address of the created token does not match the predeployed address"); diff --git a/src/spotlight-token/ISpotlightToken.sol b/src/spotlight-token/ISpotlightToken.sol index 0bf0171..0ad00cb 100644 --- a/src/spotlight-token/ISpotlightToken.sol +++ b/src/spotlight-token/ISpotlightToken.sol @@ -79,30 +79,26 @@ interface ISpotlightToken { /** * @dev Initializes the token. * - * @param owner_ The address of the token owner. + * @param tokenName_ The name of the token. + * @param tokenSymbol_ The symbol of the token. * @param tokenCreator_ The address of the token creator. * @param bondingCurve_ The address of the bonding curve. - * @param baseToken_ The address of the base token. * @param protocolFeeRecipient_ The address of the protocol fee recipient. - * @param tokenName_ The name of the token. - * @param tokenSymbol_ The symbol of the token. + * @param ipAccount_ The address of the ipAccount. + * @param _rewardsVault The address of the reward vault. * @param piperXRouter_ The address of the piperX router. * @param piperXFactory_ The address of the piperX factory. - * @param specificAddress_ The address of the specific address. - * @param protocolRewards_ The address of the protocol rewards. */ function initialize( - address owner_, + string memory tokenName_, + string memory tokenSymbol_, address tokenCreator_, address bondingCurve_, - address baseToken_, address protocolFeeRecipient_, - string memory tokenName_, - string memory tokenSymbol_, + address ipAccount_, + address _rewardsVault, address piperXRouter_, - address piperXFactory_, - address specificAddress_, - address protocolRewards_ + address piperXFactory_ ) external; /** @@ -111,14 +107,14 @@ interface ISpotlightToken { function isInitialized() external view returns (bool); /** - * @dev Returns the address of the token owner. + * @dev Returns the address of the token creator. */ - function owner() external view returns (address); + function tokenCreator() external view returns (address); /** - * @dev Returns the address of the token creator. + * @dev Returns the address of the bonding curve. */ - function tokenCreator() external view returns (address); + function bondingCurve() external view returns (address); /** * @dev Returns the address of the protocol fee recipient. @@ -126,14 +122,19 @@ interface ISpotlightToken { function protocolFeeRecipient() external view returns (address); /** - * @dev Sets the address of the protocol fee recipient. + * @dev Returns the address of the rewards vault. */ - function setProtocolFeeRecipient(address newRecipient) external; + function rewardsVault() external view returns (address); /** - * @dev Returns the address of the bonding curve. + * @dev Returns the address of the piperX router. */ - function bondingCurve() external view returns (address); + function piperXRouter() external view returns (address); + + /** + * @dev Returns the address of the piperX factory. + */ + function piperXFactory() external view returns (address); /** * @dev Returns the current market state. diff --git a/src/spotlight-token/InitializableERC20.sol b/src/spotlight-token/InitializableERC20.sol deleted file mode 100644 index 1ed69b0..0000000 --- a/src/spotlight-token/InitializableERC20.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.13; - -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -abstract contract InitializableERC20 is ERC20 { - string internal _tokenName; - string internal _tokenSymbol; - - constructor() ERC20("", "") {} - - function name() public view override returns (string memory) { - return _tokenName; - } - - function symbol() public view override returns (string memory) { - return _tokenSymbol; - } -} diff --git a/src/spotlight-token/ReentrancyGuard.sol b/src/spotlight-token/ReentrancyGuard.sol deleted file mode 100644 index f1331b7..0000000 --- a/src/spotlight-token/ReentrancyGuard.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -abstract contract ReentrancyGuard { - uint256 private constant NOT_ENTERED = 1; - uint256 private constant ENTERED = 2; - - uint256 private _status; - - /** - * @dev Initializes the contract setting the status to NOT_ENTERED. - * This should be called by the derived contract's initialize function. - */ - function __ReentrancyGuard_init() internal { - _status = NOT_ENTERED; - } - - modifier nonReentrant() { - _nonReentrantBefore(); - _; - _nonReentrantAfter(); - } - - function _nonReentrantBefore() private { - require(_status != ENTERED, "ReentrancyGuard: reentrant call"); - - _status = ENTERED; - } - - function _nonReentrantAfter() private { - _status = NOT_ENTERED; - } - - /** - * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a - * `nonReentrant` function in the call stack. - */ - function _reentrancyGuardEntered() internal view returns (bool) { - return _status == ENTERED; - } -} diff --git a/src/spotlight-token/SpotlightToken.sol b/src/spotlight-token/SpotlightToken.sol index 36c65e9..2859507 100644 --- a/src/spotlight-token/SpotlightToken.sol +++ b/src/spotlight-token/SpotlightToken.sol @@ -2,7 +2,8 @@ pragma solidity ^0.8.13; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {InitializableERC20} from "./InitializableERC20.sol"; +import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol"; +import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {ISpotlightToken} from "./ISpotlightToken.sol"; import {SpotlightTokenStorage} from "./SpotlightTokenStorage.sol"; import {ISpotlightBondingCurve} from "../spotlight-bonding-curve/ISpotlightBondingCurve.sol"; @@ -10,22 +11,18 @@ import {IWETH} from "../interfaces/IWETH.sol"; import {IUniswapV2Router02} from "../interfaces/IUniswapV2Router02.sol"; import {IUniswapV2Factory} from "../interfaces/IUniswapV2Factory.sol"; import {MarketType, MarketState} from "./ISpotlightToken.sol"; -import {ReentrancyGuard} from "./ReentrancyGuard.sol"; import {ISpotlightProtocolRewards} from "../spotlight-protocol-rewards/ISpotlightProtocolRewards.sol"; -contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenStorage, ISpotlightToken { - constructor() InitializableERC20() {} +contract SpotlightToken is ERC20Upgradeable, ReentrancyGuardTransient, SpotlightTokenStorage, ISpotlightToken { + constructor() { + _disableInitializers(); + } modifier needInitialized() { _checkIsInitialized(); _; } - modifier onlyOwner() { - _checkIsOwner(); - _; - } - error SlippageBoundsExceeded(); error IPTransferFailed(); error InvalidMarketType(); @@ -37,80 +34,75 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt * @dev See {ISpotlightToken-initialize}. */ function initialize( - address owner_, + string memory tokenName_, + string memory tokenSymbol_, address tokenCreator_, address bondingCurve_, - address baseToken_, address protocolFeeRecipient_, - string memory tokenName_, - string memory tokenSymbol_, + address ipAccount_, + address rewardsVault_, address piperXRouter_, - address piperXFactory_, - address specificAddress_, - address protocolRewards_ - ) external { - if (isInitialized()) { - revert("SpotlightToken: Already initialized"); - } - - _owner = owner_; + address piperXFactory_ + ) external initializer { + __ERC20_init(tokenName_, tokenSymbol_); _tokenCreator = tokenCreator_; - _protocolFeeRecipient = protocolFeeRecipient_; _bondingCurve = bondingCurve_; - _baseToken = baseToken_; - _tokenName = tokenName_; - _tokenSymbol = tokenSymbol_; - - _isInitialized = true; - _marketType = MarketType.BONDING_CURVE; + _protocolFeeRecipient = protocolFeeRecipient_; + _ipAccount = ipAccount_; + _rewardsVault = rewardsVault_; _piperXRouter = piperXRouter_; _piperXFactory = piperXFactory_; - _specificAddress = specificAddress_; - _protocolRewards = protocolRewards_; - __ReentrancyGuard_init(); + _marketType = MarketType.BONDING_CURVE; } - /* - * @dev See {ISpotlightToken-isInitialized}. + /** + * @dev Returns `true` if the contract is initialized. */ function isInitialized() public view returns (bool) { - return _isInitialized; + return _getInitializedVersion() == 1; } /* - * @dev See {ISpotlightToken-owner}. + * @dev See {ISpotlightToken-tokenCreator}. */ - function owner() public view needInitialized returns (address) { - return _owner; + function tokenCreator() public view returns (address) { + return _tokenCreator; } /* - * @dev See {ISpotlightToken-tokenCreator}. + * @dev See {ISpotlightToken-bondingCurve}. */ - function tokenCreator() public view needInitialized returns (address) { - return _tokenCreator; + function bondingCurve() public view returns (address) { + return _bondingCurve; } /* - * @dev See {ISpotlightToken-baseToken}. + * @dev See {ISpotlightToken-protocolFeeRecipient}. */ - function protocolFeeRecipient() public view needInitialized returns (address) { + function protocolFeeRecipient() public view returns (address) { return _protocolFeeRecipient; } /* - * @dev See {ISpotlightToken-setProtocolFeeRecipient}. + * @dev See {ISpotlightToken-rewardsVault}. */ - function setProtocolFeeRecipient(address newRecipient) external needInitialized onlyOwner { - _protocolFeeRecipient = newRecipient; + function rewardsVault() public view returns (address) { + return _rewardsVault; } /* - * @dev See {ISpotlightToken-bondingCurve}. + * @dev See {ISpotlightToken-piperXRouter}. */ - function bondingCurve() public view returns (address) { - return _bondingCurve; + function piperXRouter() public view returns (address) { + return _piperXRouter; + } + + /* + * @dev See {ISpotlightToken-piperXFactory}. + */ + function piperXFactory() public view returns (address) { + return _piperXFactory; } /* @@ -124,7 +116,7 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt } /* - * @dev See {ISpotlightToken-setBondingCurve}. + * @dev See {ISpotlightToken-buyWithIP}. */ function buyWithIP(address recipient, uint256 minTokenOut, MarketType expectedMarketType) public @@ -143,21 +135,17 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt if (_marketType == MarketType.PIPERX_POOL) { totalCost = msg.value; - IWETH(_baseToken).deposit{value: totalCost}(); - IERC20(_baseToken).approve(_piperXRouter, totalCost); address[] memory path = new address[](2); - path[0] = _baseToken; + path[0] = IUniswapV2Router02(_piperXRouter).WETH(); path[1] = address(this); - uint256[] memory amounts = IUniswapV2Router02(_piperXRouter).swapExactTokensForTokens( - totalCost, minTokenOut, path, recipient, block.timestamp + uint256[] memory amounts = IUniswapV2Router02(_piperXRouter).swapExactETHForTokens{value: totalCost}( + minTokenOut, path, recipient, block.timestamp ); trueOrderSize = amounts[1]; - } - - if (_marketType == MarketType.BONDING_CURVE) { + } else if (_marketType == MarketType.BONDING_CURVE) { bool shouldGraduateMarket; (totalCost, trueOrderSize, fee, refund, shouldGraduateMarket) = _validateBondingCurveBuy(minTokenOut); @@ -198,23 +186,18 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt if (_marketType == MarketType.PIPERX_POOL) { address[] memory path = new address[](2); - path[0] = _baseToken; + path[0] = IUniswapV2Router02(_piperXRouter).WETH(); path[1] = address(this); uint256[] memory amountIns = IUniswapV2Router02(_piperXRouter).getAmountsIn(tokenAmount, path); uint256 amountIn = amountIns[0]; - IWETH(_baseToken).deposit{value: amountIn}(); - IERC20(_baseToken).approve(_piperXRouter, amountIn); - - uint256[] memory amounts = IUniswapV2Router02(_piperXRouter).swapTokensForExactTokens( - tokenAmount, amountIn, path, recipient, block.timestamp + uint256[] memory amounts = IUniswapV2Router02(_piperXRouter).swapETHForExactTokens{value: amountIn}( + tokenAmount, path, recipient, block.timestamp ); trueOrderSize = amounts[1]; - } - - if (_marketType == MarketType.BONDING_CURVE) { + } else if (_marketType == MarketType.BONDING_CURVE) { bool shouldGraduateMarket; (totalCost, trueOrderSize, fee, refund, shouldGraduateMarket) = _validateBondingCurveBuyToken(tokenAmount); @@ -251,10 +234,8 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt } if (_marketType == MarketType.PIPERX_POOL) { - _handleUniswapSell(tokenAmount, minIPOut); - } - - if (_marketType == MarketType.BONDING_CURVE) { + _handleUniswapSell(tokenAmount, minIPOut, recipient); + } else if (_marketType == MarketType.BONDING_CURVE) { uint256 truePayoutSize; uint256 payoutAfterFee; truePayoutSize = _handleBondingCurveSell(tokenAmount, minIPOut); @@ -272,7 +253,7 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt } receive() external payable { - if (msg.sender == _baseToken) { + if (msg.sender == IUniswapV2Router02(_piperXRouter).WETH()) { return; } @@ -282,17 +263,15 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt /* * @dev See {ISpotlightToken-getIPBuyQuote}. */ - function getIPBuyQuote(uint256 ipOrderSize) public view needInitialized returns (uint256 tokensOut) { + function getIPBuyQuote(uint256 ipOrderSize) public view returns (uint256 tokensOut) { if (_marketType == MarketType.PIPERX_POOL) { address[] memory path = new address[](2); - path[0] = _baseToken; + path[0] = IUniswapV2Router02(_piperXRouter).WETH(); path[1] = address(this); uint256[] memory amounts = IUniswapV2Router02(_piperXRouter).getAmountsOut(ipOrderSize, path); tokensOut = amounts[1]; - } - - if (_marketType == MarketType.BONDING_CURVE) { + } else if (_marketType == MarketType.BONDING_CURVE) { tokensOut = ISpotlightBondingCurve(_bondingCurve).getBaseTokenBuyQuote(totalSupply(), ipOrderSize); } } @@ -300,16 +279,15 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt /* * @dev See {ISpotlightToken-getTokenBuyQuote}. */ - function getTokenBuyQuote(uint256 tokenOrderSize) public view needInitialized returns (uint256 ipIn) { + function getTokenBuyQuote(uint256 tokenOrderSize) public view returns (uint256 ipIn) { if (_marketType == MarketType.PIPERX_POOL) { address[] memory path = new address[](2); - path[0] = _baseToken; + path[0] = IUniswapV2Router02(_piperXRouter).WETH(); path[1] = address(this); uint256[] memory amounts = IUniswapV2Router02(_piperXRouter).getAmountsIn(tokenOrderSize, path); ipIn = amounts[0]; - } - if (_marketType == MarketType.BONDING_CURVE) { + } else if (_marketType == MarketType.BONDING_CURVE) { ipIn = ISpotlightBondingCurve(_bondingCurve).getTargetTokenBuyQuote(totalSupply(), tokenOrderSize); } } @@ -317,17 +295,15 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt /* * @dev See {ISpotlightToken-getTokenSellQuote}. */ - function getTokenSellQuote(uint256 tokenOrderSize) public view needInitialized returns (uint256 ipOut) { + function getTokenSellQuote(uint256 tokenOrderSize) public view returns (uint256 ipOut) { if (_marketType == MarketType.PIPERX_POOL) { address[] memory path = new address[](2); path[0] = address(this); - path[1] = _baseToken; + path[1] = IUniswapV2Router02(_piperXRouter).WETH(); uint256[] memory amounts = IUniswapV2Router02(_piperXRouter).getAmountsOut(tokenOrderSize, path); ipOut = amounts[1]; - } - - if (_marketType == MarketType.BONDING_CURVE) { + } else if (_marketType == MarketType.BONDING_CURVE) { ipOut = ISpotlightBondingCurve(_bondingCurve).getTargetTokenSellQuote(totalSupply(), tokenOrderSize); } } @@ -335,7 +311,7 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt /* * @dev See {ISpotlightToken-getIPBuyQuoteWithFee}. */ - function getIPBuyQuoteWithFee(uint256 ipOrderSize) public view needInitialized returns (uint256 tokensOut) { + function getIPBuyQuoteWithFee(uint256 ipOrderSize) public view returns (uint256 tokensOut) { if (_marketType == MarketType.PIPERX_POOL) revert InvalidMarketType(); uint256 tradingFee = _calculateFee(ipOrderSize, TOTAL_FEE_BPS); @@ -346,7 +322,7 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt /* * @dev See {ISpotlightToken-getTokenBuyQuoteWithFee}. */ - function getTokenBuyQuoteWithFee(uint256 tokenOrderSize) public view needInitialized returns (uint256 ipIn) { + function getTokenBuyQuoteWithFee(uint256 tokenOrderSize) public view returns (uint256 ipIn) { if (_marketType == MarketType.PIPERX_POOL) revert InvalidMarketType(); uint256 ipNeeded = ISpotlightBondingCurve(_bondingCurve).getTargetTokenBuyQuote(totalSupply(), tokenOrderSize); @@ -358,7 +334,7 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt /* * @dev See {ISpotlightToken-getTokenSellQuoteWithFee}. */ - function getTokenSellQuoteWithFee(uint256 tokenOrderSize) public view needInitialized returns (uint256 ipOut) { + function getTokenSellQuoteWithFee(uint256 tokenOrderSize) public view returns (uint256 ipOut) { if (_marketType == MarketType.PIPERX_POOL) revert InvalidMarketType(); uint256 ipFromTrading = @@ -367,13 +343,8 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt ipOut = ipFromTrading - tradingFee; } - // @dev Private functions - function _checkIsOwner() internal view { - require(msg.sender == _owner, "SpotlightToken: Not owner"); - } - function _checkIsInitialized() internal view { - require(_isInitialized, "SpotlightToken: Not initialized"); + require(isInitialized(), "SpotlightToken: Not initialized"); } function _handleBondingCurveSell(uint256 tokensToSell, uint256 minPayoutSize) private returns (uint256) { @@ -387,16 +358,19 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt return payout; } - function _handleUniswapSell(uint256 tokensToSell, uint256 minPayoutSize) private returns (uint256) { + function _handleUniswapSell(uint256 tokensToSell, uint256 minPayoutSize, address recipient) + private + returns (uint256) + { transfer(address(this), tokensToSell); this.approve(address(_piperXRouter), tokensToSell); address[] memory path = new address[](2); path[0] = address(this); - path[1] = _baseToken; + path[1] = IUniswapV2Router02(_piperXRouter).WETH(); uint256[] memory amounts = IUniswapV2Router02(_piperXRouter).swapExactTokensForETH( - tokensToSell, minPayoutSize, path, msg.sender, block.timestamp + tokensToSell, minPayoutSize, path, recipient, block.timestamp ); return amounts[1]; @@ -466,19 +440,19 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt } function _disperseFees(uint256 _fee) internal { - if (_specificAddress == address(0)) { + if (_ipAccount == address(0)) { (bool success,) = _protocolFeeRecipient.call{value: _fee}(""); if (!success) revert IPTransferFailed(); return; } - uint256 protocolFee = _calculateFee(_fee, PROTOCOL_TRADING_FEE_BPS); - uint256 specificAddressFee = _calculateFee(_fee, SPECIFIC_ADDRESS_FEE_BPS); + uint256 ipAccountFee = _calculateFee(_fee, IP_ACCOUNT_FEE_BPS); + uint256 protocolFee = _fee - ipAccountFee; // others are protocol fees (bool protocolSuccess,) = _protocolFeeRecipient.call{value: protocolFee}(""); if (!protocolSuccess) revert IPTransferFailed(); - ISpotlightProtocolRewards(_protocolRewards).deposit{value: specificAddressFee}(_specificAddress); + ISpotlightProtocolRewards(_rewardsVault).deposit{value: ipAccountFee}(_ipAccount); } function _graduateMarket() internal { @@ -488,17 +462,16 @@ contract SpotlightToken is InitializableERC20, ReentrancyGuard, SpotlightTokenSt if (!success) revert IPTransferFailed(); uint256 ethLiquidity = address(this).balance; - IWETH(_baseToken).deposit{value: ethLiquidity}(); - _mint(address(this), SECONDARY_MARKET_SUPPLY); - IERC20(_baseToken).approve(address(_piperXRouter), ethLiquidity); - IERC20(address(this)).approve(address(_piperXRouter), SECONDARY_MARKET_SUPPLY); + _mint(address(this), SECONDARY_MARKET_SUPPLY); + IERC20(address(this)).approve(_piperXRouter, SECONDARY_MARKET_SUPPLY); - (uint256 amountToken, uint256 amountETH, uint256 liquidity) = IUniswapV2Router02(_piperXRouter).addLiquidity( - address(this), _baseToken, SECONDARY_MARKET_SUPPLY, ethLiquidity, 0, 0, address(this), block.timestamp - ); + (uint256 amountToken, uint256 amountETH, uint256 liquidity) = IUniswapV2Router02(_piperXRouter).addLiquidityETH{ + value: ethLiquidity + }(address(this), SECONDARY_MARKET_SUPPLY, SECONDARY_MARKET_SUPPLY, ethLiquidity, address(0), block.timestamp); - _pairAddress = IUniswapV2Factory(_piperXFactory).getPair(address(this), _baseToken); + _pairAddress = + IUniswapV2Factory(_piperXFactory).getPair(address(this), IUniswapV2Router02(_piperXRouter).WETH()); emit SpotlightTokenGraduated(address(this), _pairAddress, amountETH, amountToken, liquidity, _marketType); } diff --git a/src/spotlight-token/SpotlightTokenStorage.sol b/src/spotlight-token/SpotlightTokenStorage.sol index 3454581..8a6d3c1 100644 --- a/src/spotlight-token/SpotlightTokenStorage.sol +++ b/src/spotlight-token/SpotlightTokenStorage.sol @@ -4,32 +4,24 @@ pragma solidity ^0.8.13; import {MarketType} from "./ISpotlightToken.sol"; abstract contract SpotlightTokenStorage { - // @dev v1 properties uint256 public constant BONDING_CURVE_SUPPLY = 800_000_000e18; // 0.8 billion - uint256 public constant PROTOCOL_TRADING_FEE_BPS = 9_000; // 90% + uint256 public constant SECONDARY_MARKET_SUPPLY = 200_000_000e18; // 0.2 billion + uint256 public constant MAX_TOTAL_SUPPLY = BONDING_CURVE_SUPPLY + SECONDARY_MARKET_SUPPLY; // 1 billion + + uint256 public constant TOTAL_FEE_BPS = 100; // 1% + uint256 public constant IP_ACCOUNT_FEE_BPS = 1_000; // 10% to IPAccount, 90% to protocol + uint256 public constant LISTING_FEE = 0.1 ether; + uint256 public constant MIN_IP_ORDER_SIZE = 0.0001 ether; // 0.0001 IP - address internal _owner; address internal _tokenCreator; - address internal _protocolFeeRecipient; address internal _bondingCurve; - address internal _baseToken; - bool internal _isInitialized; - // @dev end of v1 properties - - // @dev v2 properties - trading on dex - uint256 public constant MAX_TOTAL_SUPPLY = 1_000_000_000e18; // 1 billion - uint256 public constant SECONDARY_MARKET_SUPPLY = 200_000_000e18; // 0.2 billion - uint256 public constant TOTAL_FEE_BPS = 100; // 1% - + address internal _protocolFeeRecipient; + address internal _ipAccount; + address internal _rewardsVault; address internal _piperXRouter; address internal _piperXFactory; - address internal _pairAddress; - MarketType internal _marketType; - address internal _specificAddress; - uint256 public constant SPECIFIC_ADDRESS_FEE_BPS = 1_000; // 10% - uint256 public constant LISTING_FEE = 0.1 ether; - address internal _protocolRewards; - // @dev end of v2 properties + MarketType internal _marketType; + address internal _pairAddress; } diff --git a/test/ProtocolRewardsTest.t.sol b/test/ProtocolRewardsTest.t.sol index 921cfdd..d5f1286 100644 --- a/test/ProtocolRewardsTest.t.sol +++ b/test/ProtocolRewardsTest.t.sol @@ -145,7 +145,7 @@ contract SpotlightProtocolRewardsTest is Test { address[] memory ipaIds = new address[](0); vm.startPrank(_user); - vm.expectRevert(SpotlightProtocolRewards.IpaIdsEmpty.selector); + vm.expectRevert(SpotlightProtocolRewards.IPAccountsEmpty.selector); _protocolRewards.withdrawAll(ipaIds); vm.stopPrank(); } diff --git a/test/SpotlightTokenFactoryTest.t.sol b/test/SpotlightTokenFactoryTest.t.sol index 72e0ca0..acf2eab 100644 --- a/test/SpotlightTokenFactoryTest.t.sol +++ b/test/SpotlightTokenFactoryTest.t.sol @@ -204,8 +204,7 @@ contract SpotlightTokenFactoryTest is Test { uint256 SHOULD_REFUND_EXCESS_IP = 1 ether; uint256 TOKEN_CREATOR_BALANCE = (DEFAULT_CREATION_FEE + INITIAL_BUY_AMOUNT + SHOULD_REFUND_EXCESS_IP); - uint256 expectedFactoryBalance = address(_factory).balance + DEFAULT_CREATION_FEE; - uint256 expectedFactoryOwnerBalance = INITIAL_BUY_AMOUNT * 1 / 100; + uint256 expectedFactoryBalance = address(_factory).balance + DEFAULT_CREATION_FEE + INITIAL_BUY_AMOUNT * 1 / 100; uint256 expectedTokenCreatorBalance = SHOULD_REFUND_EXCESS_IP; uint256 expectedPredeployedTokenBalance = INITIAL_BUY_AMOUNT * 99 / 100; @@ -230,7 +229,6 @@ contract SpotlightTokenFactoryTest is Test { assertEq(address(_factory).balance, expectedFactoryBalance); assertEq(tokenCreator.balance, expectedTokenCreatorBalance); - assertEq(_factoryOwner.balance, expectedFactoryOwnerBalance); assertEq(predeployedTokenAddress.balance, expectedPredeployedTokenBalance); assertEq(_factory.numberOfTokensCreated(tokenCreator), 1); } @@ -241,7 +239,6 @@ contract SpotlightTokenFactoryTest is Test { uint256 EXCESS_INITIAL_BUY_AMOUNT = 1 ether; uint256 expectedFactoryBalance = address(_factory).balance; - uint256 expectedFactoryOwnerBalance = 0; uint256 expectedPredeployedTokenBalance = 0; address tokenCreator = makeAddr("tokenCreator"); @@ -265,7 +262,6 @@ contract SpotlightTokenFactoryTest is Test { vm.stopPrank(); assertEq(address(_factory).balance, expectedFactoryBalance); - assertEq(_factoryOwner.balance, expectedFactoryOwnerBalance); assertEq(predeployedTokenAddress.balance, expectedPredeployedTokenBalance); } diff --git a/test/SpotlightTokenTest.t.sol b/test/SpotlightTokenTest.t.sol index 6892a3b..0ee998b 100644 --- a/test/SpotlightTokenTest.t.sol +++ b/test/SpotlightTokenTest.t.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.13; import "../lib/forge-std/src/Test.sol"; +import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {SpotlightToken} from "../src/spotlight-token/SpotlightToken.sol"; import {MarketType, MarketState} from "../src/spotlight-token/ISpotlightToken.sol"; import {SpotlightTokenIPCollection} from "../src/spotlight-token-collection/SpotlightTokenIPCollection.sol"; @@ -25,17 +27,16 @@ contract SpotlightTokenTest is Test { uint256 public constant BONDING_CURVE_SUPPLY = 800_000_000e18; // 0.8 billion uint256 public constant MAX_TOTAL_SUPPLY = 1_000_000_000e18; // 1 billion uint256 public constant GRADUATE_MARKET_AMOUNT = 3 ether; - uint256 public constant SPECIFIC_ADDRESS_FEE_BPS = 1_000; // 10% - uint256 public constant PROTOCOL_TRADING_FEE_BPS = 9_000; // 90% - address constant IPA_ID = 0x359EcA9F3C4cCdB7C10Dd4D9410EaD52Ef9B430A; + uint256 public constant IP_ACCOUNT_FEE_BPS = 1_000; // 10% + address constant IP_ACCOUNT = 0x359EcA9F3C4cCdB7C10Dd4D9410EaD52Ef9B430A; SpotlightTokenFactory private _factory; SpotlightTokenIPCollection private _tokenIpCollection; SpotlightNativeBondingCurve private _bondingCurve; SpotlightToken private _token; - SpotlightToken private _tokenCreatedWithSpecificAddress; + SpotlightToken private _tokenCreatedWithIPAccount; MockStoryDerivativeWorkflows private _mockStoryWorkflows; - SpotlightProtocolRewards private _protocolRewards; + SpotlightProtocolRewards private _rewardsVault; address private _factoryOwner; address private _tokenCreator; @@ -51,7 +52,7 @@ contract SpotlightTokenTest is Test { _factory = new SpotlightTokenFactory(); _tokenIpCollection = new SpotlightTokenIPCollection(address(_factory)); _bondingCurve = new SpotlightNativeBondingCurve(690_000_000, 2_878_200_000); - _protocolRewards = new SpotlightProtocolRewards(); + _rewardsVault = new SpotlightProtocolRewards(); _factory.initialize( _factoryOwner, @@ -63,7 +64,7 @@ contract SpotlightTokenTest is Test { address(_mockStoryWorkflows), PIPERX_V2_ROUTER, PIPERX_V2_FACTORY, - address(_protocolRewards) + address(_rewardsVault) ); vm.stopPrank(); @@ -93,49 +94,80 @@ contract SpotlightTokenTest is Test { ); _token = SpotlightToken(payable(_tokenAddress)); - address predeployedTokenAddressWithSpecificAddress = _factory.calculateTokenAddress(_tokenCreator); - ISpotlightTokenFactory.TokenCreationData memory tokenCreationDataWithSpecificAddress = ISpotlightTokenFactory + address predeployedTokenAddressWithIPAccount = _factory.calculateTokenAddress(_tokenCreator); + ISpotlightTokenFactory.TokenCreationData memory tokenCreationDataWithIPAccount = ISpotlightTokenFactory .TokenCreationData({ tokenIpNFTId: 2, tokenName: "Test Token", tokenSymbol: "TEST", - predeployedTokenAddress: predeployedTokenAddressWithSpecificAddress + predeployedTokenAddress: predeployedTokenAddressWithIPAccount }); - address tokenAddressWithSpecificAddress; - (tokenAddressWithSpecificAddress,) = _factory.createToken{value: DEFAULT_CREATION_FEE}( - tokenCreationDataWithSpecificAddress, + address tokenCreatedWithIPAccount; + (tokenCreatedWithIPAccount,) = _factory.createToken{value: DEFAULT_CREATION_FEE}( + tokenCreationDataWithIPAccount, initialBuyData, makeDerivative, ipMetadata, sigMetadata, sigRegister, - IPA_ID + IP_ACCOUNT ); - _tokenCreatedWithSpecificAddress = SpotlightToken(payable(tokenAddressWithSpecificAddress)); + _tokenCreatedWithIPAccount = SpotlightToken(payable(tokenCreatedWithIPAccount)); vm.stopPrank(); _buyer = makeAddr("buyer"); } + function testImplementationIsInitialized() public { + SpotlightToken token = new SpotlightToken(); + assertFalse(token.isInitialized()); + } + + function testProxyInitialization() public { + SpotlightToken token = new SpotlightToken(); + SpotlightToken proxy = SpotlightToken(payable(Clones.clone(address(token)))); + assertFalse(proxy.isInitialized()); + + vm.expectRevert("SpotlightToken: Not initialized"); + proxy.buyWithIP(address(0), 0, MarketType.BONDING_CURVE); + vm.expectRevert("SpotlightToken: Not initialized"); + proxy.buyToken(0, address(0), MarketType.BONDING_CURVE); + vm.expectRevert("SpotlightToken: Not initialized"); + proxy.sellToken(0, address(0), 0, MarketType.BONDING_CURVE); + + proxy.initialize( + "Test Token", + "TEST", + _tokenCreator, + address(_bondingCurve), + address(_factory), + address(IP_ACCOUNT), + address(_rewardsVault), + address(PIPERX_V2_ROUTER), + address(PIPERX_V2_FACTORY) + ); + } + function testGetterFunctions() public view { - assertTrue(_token.isInitialized()); - assertEq(_token.owner(), _factoryOwner); assertEq(_token.name(), "Test Token"); assertEq(_token.symbol(), "TEST"); assertEq(_token.tokenCreator(), _tokenCreator); - assertEq(_token.protocolFeeRecipient(), _factoryOwner); + assertEq(_token.protocolFeeRecipient(), address(_factory)); assertEq(_token.bondingCurve(), address(_bondingCurve)); + assertEq(_token.rewardsVault(), address(_rewardsVault)); + assertEq(_token.piperXRouter(), PIPERX_V2_ROUTER); + assertEq(_token.piperXFactory(), PIPERX_V2_FACTORY); } function testBuyWithIPSuccessInBondingCurvePhase() public { uint256 USER_BUY_AMOUNT = 1 ether; uint256 PROTOCOL_TRADING_FEE = _calculateFee(USER_BUY_AMOUNT, TOTAL_FEE_BPS); uint256 TOKEN_CONTRACT_BALANCE_BEFORE = address(_token).balance; - uint256 FACTORY_OWNER_BALANCE_BEFORE = _factoryOwner.balance; + uint256 FACTORY_BALANCE_BEFORE = address(_factory).balance; uint256 expectedTokenReceived = _token.getIPBuyQuoteWithFee(USER_BUY_AMOUNT); uint256 expectedContractIPBalance = TOKEN_CONTRACT_BALANCE_BEFORE + USER_BUY_AMOUNT - PROTOCOL_TRADING_FEE; - uint256 expectedFactoryOwnerBalance = FACTORY_OWNER_BALANCE_BEFORE + PROTOCOL_TRADING_FEE; + uint256 expectedFactoryBalance = FACTORY_BALANCE_BEFORE + PROTOCOL_TRADING_FEE; vm.deal(_buyer, USER_BUY_AMOUNT); vm.startPrank(_buyer); @@ -156,7 +188,7 @@ contract SpotlightTokenTest is Test { assertEq(_token.balanceOf(_buyer), expectedTokenReceived); assertEq(address(_token).balance, expectedContractIPBalance); - assertEq(address(_factoryOwner).balance, expectedFactoryOwnerBalance); + assertEq(address(_factory).balance, expectedFactoryBalance); } function testBuyWithIPSuccessGraduateMarketInBondingCurvePhase() public { @@ -187,21 +219,20 @@ contract SpotlightTokenTest is Test { assertApproxEqAbs(_buyer.balance, expectedRefundBuyerReceived, 1e18); } - function testBuyWithIPSuccessWithDisperseFeeToSpecificAddress() public { + function testBuyWithIPSuccessWithDisperseFeeToIPAccountOwner() public { uint256 USER_BUY_AMOUNT = 1 ether; - uint256 PROTOCOL_TRADING_FEE = _calculateFee(USER_BUY_AMOUNT, TOTAL_FEE_BPS); - uint256 expectedFactoryOwnerBalance = - _factoryOwner.balance + _calculateFee(PROTOCOL_TRADING_FEE, PROTOCOL_TRADING_FEE_BPS); - uint256 expectedProtocolRewardsBalance = - address(_protocolRewards).balance + _calculateFee(PROTOCOL_TRADING_FEE, SPECIFIC_ADDRESS_FEE_BPS); + uint256 protocolTradingFee = _calculateFee(USER_BUY_AMOUNT, TOTAL_FEE_BPS); + uint256 ipAccountReward = _calculateFee(protocolTradingFee, IP_ACCOUNT_FEE_BPS); + uint256 expectedProtocolRewardsBalance = address(_rewardsVault).balance + ipAccountReward; + uint256 expectedFactoryBalance = address(_factory).balance + (protocolTradingFee - ipAccountReward); vm.deal(_buyer, USER_BUY_AMOUNT); vm.startPrank(_buyer); - _tokenCreatedWithSpecificAddress.buyWithIP{value: USER_BUY_AMOUNT}(_buyer, 0, MarketType.BONDING_CURVE); + _tokenCreatedWithIPAccount.buyWithIP{value: USER_BUY_AMOUNT}(_buyer, 0, MarketType.BONDING_CURVE); vm.stopPrank(); - assertEq(address(_factoryOwner).balance, expectedFactoryOwnerBalance); - assertEq(address(_protocolRewards).balance, expectedProtocolRewardsBalance); + assertEq(address(_factory).balance, expectedFactoryBalance); + assertEq(address(_rewardsVault).balance, expectedProtocolRewardsBalance); } function testBuyWithIPSuccessInPiperXPhase() public { @@ -265,12 +296,12 @@ contract SpotlightTokenTest is Test { function testBuyTokenSuccessInBondingCurvePhase() public { uint256 USER_BUY_TOKEN_AMOUNT = 400_000_000e18; uint256 TOKEN_CONTRACT_BALANCE_BEFORE = address(_token).balance; - uint256 FACTORY_OWNER_BALANCE_BEFORE = _factoryOwner.balance; + uint256 FACTORY_BALANCE_BEFORE = address(_factory).balance; uint256 ipIn = _token.getTokenBuyQuote(USER_BUY_TOKEN_AMOUNT); uint256 protocolTradingFee = _calculateFee(ipIn, TOTAL_FEE_BPS); uint256 ipInWithFee = ipIn + protocolTradingFee; uint256 expectedContractIPBalance = TOKEN_CONTRACT_BALANCE_BEFORE + ipIn; - uint256 expectedFactoryOwnerBalance = FACTORY_OWNER_BALANCE_BEFORE + protocolTradingFee; + uint256 expectedFactoryBalance = FACTORY_BALANCE_BEFORE + protocolTradingFee; vm.deal(_buyer, ipInWithFee); vm.startPrank(_buyer); @@ -291,7 +322,7 @@ contract SpotlightTokenTest is Test { assertEq(_token.balanceOf(_buyer), USER_BUY_TOKEN_AMOUNT); assertEq(address(_token).balance, expectedContractIPBalance); - assertEq(address(_factoryOwner).balance, expectedFactoryOwnerBalance); + assertEq(address(_factory).balance, expectedFactoryBalance); } function testBuyTokenSuccessGraduateMarketInBondingCurvePhase() public { @@ -323,26 +354,23 @@ contract SpotlightTokenTest is Test { assertApproxEqAbs(_buyer.balance, expectedRefundBuyerReceived, 1e18); } - function testBuyTokenSuccessWithDisperseFeeToSpecificAddress() public { + function testBuyTokenSuccessWithDisperseFeeToIPAccountOwner() public { uint256 USER_BUY_TOKEN_AMOUNT = 400_000_000e18; - uint256 FACTORY_OWNER_BALANCE_BEFORE = _factoryOwner.balance; - uint256 ipIn = _tokenCreatedWithSpecificAddress.getTokenBuyQuote(USER_BUY_TOKEN_AMOUNT); + uint256 FACTORY_BALANCE_BEFORE = address(_factory).balance; + uint256 ipIn = _tokenCreatedWithIPAccount.getTokenBuyQuote(USER_BUY_TOKEN_AMOUNT); uint256 protocolTradingFee = _calculateFee(ipIn, TOTAL_FEE_BPS); uint256 ipInWithFee = ipIn + protocolTradingFee; - uint256 expectedFactoryOwnerBalance = - FACTORY_OWNER_BALANCE_BEFORE + _calculateFee(protocolTradingFee, PROTOCOL_TRADING_FEE_BPS); - uint256 expectedProtocolRewardsBalance = - address(_protocolRewards).balance + _calculateFee(protocolTradingFee, SPECIFIC_ADDRESS_FEE_BPS); + uint256 ipAccountReward = _calculateFee(protocolTradingFee, IP_ACCOUNT_FEE_BPS); + uint256 expectedProtocolRewardsBalance = address(_rewardsVault).balance + ipAccountReward; + uint256 expectedFactoryBalance = FACTORY_BALANCE_BEFORE + (protocolTradingFee - ipAccountReward); vm.deal(_buyer, ipInWithFee); vm.startPrank(_buyer); - _tokenCreatedWithSpecificAddress.buyToken{value: ipInWithFee}( - USER_BUY_TOKEN_AMOUNT, _buyer, MarketType.BONDING_CURVE - ); + _tokenCreatedWithIPAccount.buyToken{value: ipInWithFee}(USER_BUY_TOKEN_AMOUNT, _buyer, MarketType.BONDING_CURVE); vm.stopPrank(); - assertEq(address(_factoryOwner).balance, expectedFactoryOwnerBalance); - assertEq(address(_protocolRewards).balance, expectedProtocolRewardsBalance); + assertEq(address(_factory).balance, expectedFactoryBalance); + assertEq(address(_rewardsVault).balance, expectedProtocolRewardsBalance); } function testBuyTokenSuccessInPiperXPhase() public { @@ -456,31 +484,29 @@ contract SpotlightTokenTest is Test { assertEq(_buyer.balance, expectedBuyerBalance); } - function testSellTokenSuccessWithDisperseFeeToSpecificAddress() public { + function testSellTokenSuccessWithDisperseFeeToIPAccountOwner() public { uint256 USER_BUY_TOKEN_AMOUNT = 600_000_000e18; - uint256 ipInWithFee = _tokenCreatedWithSpecificAddress.getTokenBuyQuoteWithFee(USER_BUY_TOKEN_AMOUNT); + uint256 ipInWithFee = _tokenCreatedWithIPAccount.getTokenBuyQuoteWithFee(USER_BUY_TOKEN_AMOUNT); vm.deal(_buyer, ipInWithFee); vm.startPrank(_buyer); - _tokenCreatedWithSpecificAddress.buyToken{value: ipInWithFee}( - USER_BUY_TOKEN_AMOUNT, _buyer, MarketType.BONDING_CURVE - ); + _tokenCreatedWithIPAccount.buyToken{value: ipInWithFee}(USER_BUY_TOKEN_AMOUNT, _buyer, MarketType.BONDING_CURVE); vm.stopPrank(); - assertEq(_tokenCreatedWithSpecificAddress.balanceOf(_buyer), USER_BUY_TOKEN_AMOUNT); + assertEq(_tokenCreatedWithIPAccount.balanceOf(_buyer), USER_BUY_TOKEN_AMOUNT); uint256 USER_SELL_TOKEN_AMOUNT = 500_000_000e18; - uint256 ipOut = _tokenCreatedWithSpecificAddress.getTokenSellQuote(USER_SELL_TOKEN_AMOUNT); + uint256 ipOut = _tokenCreatedWithIPAccount.getTokenSellQuote(USER_SELL_TOKEN_AMOUNT); uint256 fee = _calculateFee(ipOut, TOTAL_FEE_BPS); - uint256 expectedProtocolRewardsBalance = - address(_protocolRewards).balance + _calculateFee(fee, SPECIFIC_ADDRESS_FEE_BPS); - uint256 expectedFactoryOwnerBalance = _factoryOwner.balance + _calculateFee(fee, PROTOCOL_TRADING_FEE_BPS); + uint256 ipAccountReward = _calculateFee(fee, IP_ACCOUNT_FEE_BPS); + uint256 expectedProtocolRewardsBalance = address(_rewardsVault).balance + ipAccountReward; + uint256 expectedFactoryBalance = address(_factory).balance + (fee - ipAccountReward); vm.startPrank(_buyer); - _tokenCreatedWithSpecificAddress.sellToken(USER_SELL_TOKEN_AMOUNT, _buyer, 0, MarketType.BONDING_CURVE); + _tokenCreatedWithIPAccount.sellToken(USER_SELL_TOKEN_AMOUNT, _buyer, 0, MarketType.BONDING_CURVE); vm.stopPrank(); - assertEq(address(_protocolRewards).balance, expectedProtocolRewardsBalance); - assertEq(address(_factoryOwner).balance, expectedFactoryOwnerBalance); + assertEq(address(_rewardsVault).balance, expectedProtocolRewardsBalance); + assertEq(address(_factory).balance, expectedFactoryBalance); } function testSellTokenSuccessInPiperXPhase() public {