diff --git a/contracts/solidity/NFTXInventoryStaking.sol b/contracts/solidity/NFTXInventoryStaking.sol index e00c7c7e..b4331f90 100644 --- a/contracts/solidity/NFTXInventoryStaking.sol +++ b/contracts/solidity/NFTXInventoryStaking.sol @@ -15,7 +15,6 @@ import "./proxy/UpgradeableBeacon.sol"; import "./proxy/Create2BeaconProxy.sol"; import "./token/XTokenUpgradeable.sol"; import "./interface/ITimelockExcludeList.sol"; -import "./interface/INFTXSimpleFeeDistributor.sol"; // Author: 0xKiwi. @@ -121,7 +120,6 @@ contract NFTXInventoryStaking is PausableUpgradeable, UpgradeableBeacon, INFTXIn // Leave the bar. Claim back your tokens. // Unlocks the staked + gained tokens and burns xTokens. function withdraw(uint256 vaultId, uint256 _share) external virtual override { - _distributeFees(vaultId); IERC20Upgradeable baseToken = IERC20Upgradeable(nftxVaultFactory.vault(vaultId)); XTokenUpgradeable xToken = XTokenUpgradeable(xTokenAddr(address(baseToken))); @@ -169,8 +167,6 @@ contract NFTXInventoryStaking is PausableUpgradeable, UpgradeableBeacon, INFTXIn IERC20Upgradeable baseToken = IERC20Upgradeable(nftxVaultFactory.vault(vaultId)); XTokenUpgradeable xToken = XTokenUpgradeable((xTokenAddr(address(baseToken)))); - _distributeFees(vaultId); - uint256 xTokensMinted = xToken.mintXTokens(account, _amount, timelockLength); return (baseToken, xToken, xTokensMinted); } @@ -194,28 +190,4 @@ contract NFTXInventoryStaking is PausableUpgradeable, UpgradeableBeacon, INFTXIn assembly { size := extcodesize(account) } return size != 0; } - - function _distributeFees(uint256 vaultId) internal { - INFTXSimpleFeeDistributor(nftxVaultFactory.feeDistributor()).distribute(vaultId); - } - - function totalUndistributedFees(uint256 vaultId) public view returns (uint256) { - INFTXSimpleFeeDistributor feeDistrib = INFTXSimpleFeeDistributor(nftxVaultFactory.feeDistributor()); - (address receiverAddr, uint256 receiverAlloc) = feeDistrib.feeReceiverInfo(1); - require(receiverAddr == address(this), "wrong index"); - // TODO: fetch allocationtotal from fee distributor - return IERC20Upgradeable(nftxVaultFactory.vault(vaultId)).balanceOf(nftxVaultFactory.feeDistributor()) * receiverAlloc / 1e18; - } - - function adjustedXTokenShareValue(uint256 vaultId) public view returns (uint256) { - IERC20Upgradeable baseToken = IERC20Upgradeable(nftxVaultFactory.vault(vaultId)); - XTokenUpgradeable xToken = XTokenUpgradeable(xTokenAddr(address(baseToken))); - require(address(xToken) != address(0), "XToken not deployed"); - - uint256 multiplier = 10 ** 18; - uint256 adjustedBaseTokenBal = baseToken.balanceOf(address(xToken)) + totalUndistributedFees(vaultId); - return xToken.totalSupply() > 0 - ? multiplier * adjustedBaseTokenBal / xToken.totalSupply() - : multiplier; - } } diff --git a/contracts/solidity/NFTXLPStaking.sol b/contracts/solidity/NFTXLPStaking.sol index 17bc2973..a32f1663 100644 --- a/contracts/solidity/NFTXLPStaking.sol +++ b/contracts/solidity/NFTXLPStaking.sol @@ -11,8 +11,6 @@ import "./util/Address.sol"; import "./proxy/ClonesUpgradeable.sol"; import "./StakingTokenProvider.sol"; import "./token/TimelockRewardDistributionTokenImpl.sol"; -import "./interface/INFTXSimpleFeeDistributor.sol"; -import "./interface/INFTXVault.sol"; // Author: 0xKiwi. @@ -123,8 +121,6 @@ contract NFTXLPStaking is PausableUpgradeable { // Check the pool in case its been updated. updatePoolForVault(vaultId); - _distributeFees(vaultId); - StakingPool memory pool = vaultStakingInfo[vaultId]; require(pool.stakingToken != address(0), "LPStaking: Nonexistent pool"); IERC20Upgradeable(pool.stakingToken).safeTransferFrom(msg.sender, address(this), amount); @@ -149,9 +145,6 @@ contract NFTXLPStaking is PausableUpgradeable { onlyOwnerIfPaused(10); // Check the pool in case its been updated. updatePoolForVault(vaultId); - - _distributeFees(vaultId); - StakingPool memory pool = vaultStakingInfo[vaultId]; require(pool.stakingToken != address(0), "LPStaking: Nonexistent pool"); IERC20Upgradeable(pool.stakingToken).safeTransferFrom(msg.sender, address(this), amount); @@ -160,7 +153,6 @@ contract NFTXLPStaking is PausableUpgradeable { function exit(uint256 vaultId) external { StakingPool memory pool = vaultStakingInfo[vaultId]; - _distributeFees(vaultId); _claimRewards(pool, msg.sender); _withdraw(pool, balanceOf(vaultId, msg.sender), msg.sender); } @@ -169,7 +161,6 @@ contract NFTXLPStaking is PausableUpgradeable { StakingPool memory pool = StakingPool(_stakingToken, _rewardToken); TimelockRewardDistributionTokenImpl dist = _rewardDistributionTokenAddr(pool); require(isContract(address(dist)), "Not a pool"); - _distributeFees(INFTXVault(_rewardToken).vaultId()); _claimRewards(pool, msg.sender); _withdraw(pool, dist.balanceOf(msg.sender), msg.sender); } @@ -181,47 +172,45 @@ contract NFTXLPStaking is PausableUpgradeable { _withdraw(pool, dist.balanceOf(msg.sender), msg.sender); } - // function emergencyMigrate(uint256 vaultId) external { - // StakingPool memory pool = vaultStakingInfo[vaultId]; - // IRewardDistributionToken unusedDist = _unusedRewardDistributionTokenAddr(pool); - // IRewardDistributionToken oldDist = _oldRewardDistributionTokenAddr(pool); - - // uint256 unusedDistBal; - // if (isContract(address(unusedDist))) { - // unusedDistBal = unusedDist.balanceOf(msg.sender); - // if (unusedDistBal > 0) { - // unusedDist.burnFrom(msg.sender, unusedDistBal); - // } - // } - // uint256 oldDistBal; - // if (isContract(address(oldDist))) { - // oldDistBal = oldDist.balanceOf(msg.sender); - // if (oldDistBal > 0) { - // oldDist.withdrawReward(msg.sender); - // oldDist.burnFrom(msg.sender, oldDistBal); - // } - // } + function emergencyMigrate(uint256 vaultId) external { + StakingPool memory pool = vaultStakingInfo[vaultId]; + IRewardDistributionToken unusedDist = _unusedRewardDistributionTokenAddr(pool); + IRewardDistributionToken oldDist = _oldRewardDistributionTokenAddr(pool); + + uint256 unusedDistBal; + if (isContract(address(unusedDist))) { + unusedDistBal = unusedDist.balanceOf(msg.sender); + if (unusedDistBal > 0) { + unusedDist.burnFrom(msg.sender, unusedDistBal); + } + } + uint256 oldDistBal; + if (isContract(address(oldDist))) { + oldDistBal = oldDist.balanceOf(msg.sender); + if (oldDistBal > 0) { + oldDist.withdrawReward(msg.sender); + oldDist.burnFrom(msg.sender, oldDistBal); + } + } - // TimelockRewardDistributionTokenImpl newDist = _rewardDistributionTokenAddr(pool); - // if (!isContract(address(newDist))) { - // address deployedDist = _deployDividendToken(pool); - // require(deployedDist == address(newDist), "Not deploying proper distro"); - // emit PoolUpdated(vaultId, deployedDist); - // } - // require(unusedDistBal + oldDistBal > 0, "Nothing to migrate"); - // newDist.mint(msg.sender, unusedDistBal + oldDistBal); - // } + TimelockRewardDistributionTokenImpl newDist = _rewardDistributionTokenAddr(pool); + if (!isContract(address(newDist))) { + address deployedDist = _deployDividendToken(pool); + require(deployedDist == address(newDist), "Not deploying proper distro"); + emit PoolUpdated(vaultId, deployedDist); + } + require(unusedDistBal + oldDistBal > 0, "Nothing to migrate"); + newDist.mint(msg.sender, unusedDistBal + oldDistBal); + } function withdraw(uint256 vaultId, uint256 amount) external { StakingPool memory pool = vaultStakingInfo[vaultId]; - _distributeFees(vaultId); _claimRewards(pool, msg.sender); _withdraw(pool, amount, msg.sender); } function claimRewards(uint256 vaultId) public { StakingPool memory pool = vaultStakingInfo[vaultId]; - _distributeFees(vaultId); _claimRewards(pool, msg.sender); } @@ -240,29 +229,29 @@ contract NFTXLPStaking is PausableUpgradeable { return _rewardDistributionTokenAddr(pool); } -// function rewardDistributionToken(uint256 vaultId) external view returns (IRewardDistributionToken) { -// StakingPool memory pool = vaultStakingInfo[vaultId]; -// if (pool.stakingToken == address(0)) { -// return IRewardDistributionToken(address(0)); -// } -// return _unusedRewardDistributionTokenAddr(pool); -// } - -// function oldRewardDistributionToken(uint256 vaultId) external view returns (address) { -// StakingPool memory pool = vaultStakingInfo[vaultId]; -// if (pool.stakingToken == address(0)) { -// return address(0); -// } -// return address(_oldRewardDistributionTokenAddr(pool)); -// } - -// function unusedRewardDistributionToken(uint256 vaultId) external view returns (address) { -// StakingPool memory pool = vaultStakingInfo[vaultId]; -// if (pool.stakingToken == address(0)) { -// return address(0); -// } -// return address(_unusedRewardDistributionTokenAddr(pool)); -// } + function rewardDistributionToken(uint256 vaultId) external view returns (IRewardDistributionToken) { + StakingPool memory pool = vaultStakingInfo[vaultId]; + if (pool.stakingToken == address(0)) { + return IRewardDistributionToken(address(0)); + } + return _unusedRewardDistributionTokenAddr(pool); + } + + function oldRewardDistributionToken(uint256 vaultId) external view returns (address) { + StakingPool memory pool = vaultStakingInfo[vaultId]; + if (pool.stakingToken == address(0)) { + return address(0); + } + return address(_oldRewardDistributionTokenAddr(pool)); + } + + function unusedRewardDistributionToken(uint256 vaultId) external view returns (address) { + StakingPool memory pool = vaultStakingInfo[vaultId]; + if (pool.stakingToken == address(0)) { + return address(0); + } + return address(_unusedRewardDistributionTokenAddr(pool)); + } function rewardDistributionTokenAddr(address stakedToken, address rewardToken) public view returns (address) { StakingPool memory pool = StakingPool(stakedToken, rewardToken); @@ -276,19 +265,19 @@ contract NFTXLPStaking is PausableUpgradeable { return dist.balanceOf(addr); } - /* function oldBalanceOf(uint256 vaultId, address addr) public view returns (uint256) { + function oldBalanceOf(uint256 vaultId, address addr) public view returns (uint256) { StakingPool memory pool = vaultStakingInfo[vaultId]; IRewardDistributionToken dist = _oldRewardDistributionTokenAddr(pool); require(isContract(address(dist)), "Not a pool"); return dist.balanceOf(addr); - } */ + } - /* function unusedBalanceOf(uint256 vaultId, address addr) public view returns (uint256) { + function unusedBalanceOf(uint256 vaultId, address addr) public view returns (uint256) { StakingPool memory pool = vaultStakingInfo[vaultId]; IRewardDistributionToken dist = _unusedRewardDistributionTokenAddr(pool); require(isContract(address(dist)), "Not a pool"); return dist.balanceOf(addr); - } */ + } function lockedUntil(uint256 vaultId, address who) external view returns (uint256) { StakingPool memory pool = vaultStakingInfo[vaultId]; @@ -332,22 +321,18 @@ contract NFTXLPStaking is PausableUpgradeable { return TimelockRewardDistributionTokenImpl(tokenAddr); } - // // Note: this function does not guarantee the token is deployed, we leave that check to elsewhere to save gas. - // function _oldRewardDistributionTokenAddr(StakingPool memory pool) public view returns (IRewardDistributionToken) { - // bytes32 salt = keccak256(abi.encodePacked(pool.stakingToken, pool.rewardToken, uint256(1))); - // address tokenAddr = ClonesUpgradeable.predictDeterministicAddress(address(rewardDistTokenImpl), salt); - // return IRewardDistributionToken(tokenAddr); - // } - - // // Note: this function does not guarantee the token is deployed, we leave that check to elsewhere to save gas. - // function _unusedRewardDistributionTokenAddr(StakingPool memory pool) public view returns (IRewardDistributionToken) { - // bytes32 salt = keccak256(abi.encodePacked(pool.stakingToken, pool.rewardToken)); - // address tokenAddr = ClonesUpgradeable.predictDeterministicAddress(address(rewardDistTokenImpl), salt); - // return IRewardDistributionToken(tokenAddr); - // } + // Note: this function does not guarantee the token is deployed, we leave that check to elsewhere to save gas. + function _oldRewardDistributionTokenAddr(StakingPool memory pool) public view returns (IRewardDistributionToken) { + bytes32 salt = keccak256(abi.encodePacked(pool.stakingToken, pool.rewardToken, uint256(1))); + address tokenAddr = ClonesUpgradeable.predictDeterministicAddress(address(rewardDistTokenImpl), salt); + return IRewardDistributionToken(tokenAddr); + } - function _distributeFees(uint256 vaultId) internal { - INFTXSimpleFeeDistributor(nftxVaultFactory.feeDistributor()).distribute(vaultId); + // Note: this function does not guarantee the token is deployed, we leave that check to elsewhere to save gas. + function _unusedRewardDistributionTokenAddr(StakingPool memory pool) public view returns (IRewardDistributionToken) { + bytes32 salt = keccak256(abi.encodePacked(pool.stakingToken, pool.rewardToken)); + address tokenAddr = ClonesUpgradeable.predictDeterministicAddress(address(rewardDistTokenImpl), salt); + return IRewardDistributionToken(tokenAddr); } function isContract(address account) internal view returns (bool) { @@ -367,27 +352,4 @@ contract NFTXLPStaking is PausableUpgradeable { xSlp.burnFrom(from, amount); xSlp.mint(to, amount); } - - function totalUndistributedFees(uint256 vaultId) public view returns (uint256) { - INFTXSimpleFeeDistributor feeDistrib = INFTXSimpleFeeDistributor(nftxVaultFactory.feeDistributor()); - (address receiverAddr, uint256 receiverAlloc) = feeDistrib.feeReceiverInfo(0); - require(receiverAddr == address(this), "wrong index"); - // TODO: fetch allocationtotal from fee distributor - return IERC20Upgradeable(vaultStakingInfo[vaultId].rewardToken).balanceOf(nftxVaultFactory.feeDistributor()) * receiverAlloc / 1e18; - } - - function undistributedFees(uint256 vaultId, address staker) public view returns (uint256) { - TimelockRewardDistributionTokenImpl xSlp = _rewardDistributionTokenAddr(vaultStakingInfo[vaultId]); - uint256 totalSupply = xSlp.totalSupply(); - if (totalSupply == 0) { - return 0; - } - uint256 stakerPortion = xSlp.balanceOf(staker) * 1e18 / totalSupply; - return totalUndistributedFees(vaultId) * stakerPortion / 1e18; - } - - function adjustedDividendOf(uint256 vaultId, address staker) public view returns (uint256) { - TimelockRewardDistributionTokenImpl xSlp = _rewardDistributionTokenAddr(vaultStakingInfo[vaultId]); - return undistributedFees(vaultId, staker) + xSlp.dividendOf(staker); - } } \ No newline at end of file diff --git a/contracts/solidity/NFTXSimpleFeeDistributor.sol b/contracts/solidity/NFTXSimpleFeeDistributor.sol index da5c491d..2a7e77f5 100644 --- a/contracts/solidity/NFTXSimpleFeeDistributor.sol +++ b/contracts/solidity/NFTXSimpleFeeDistributor.sol @@ -58,10 +58,6 @@ contract NFTXSimpleFeeDistributor is INFTXSimpleFeeDistributor, ReentrancyGuardU return; } - if (tokenBalance == 0) { - return; - } - uint256 length = feeReceivers.length; uint256 leftover; for (uint256 i; i < length; ++i) { @@ -177,8 +173,4 @@ contract NFTXSimpleFeeDistributor is INFTXSimpleFeeDistributor, ReentrancyGuardU return true; } } - - function feeReceiverInfo(uint256 index) external override view returns (address, uint256) { - return (feeReceivers[index].receiver, feeReceivers[index].allocPoint); - } } \ No newline at end of file diff --git a/contracts/solidity/NFTXVaultUpgradeable.sol b/contracts/solidity/NFTXVaultUpgradeable.sol index d803a118..2502c4f1 100644 --- a/contracts/solidity/NFTXVaultUpgradeable.sol +++ b/contracts/solidity/NFTXVaultUpgradeable.sol @@ -6,7 +6,7 @@ import "./interface/INFTXVault.sol"; import "./interface/INFTXVaultFactory.sol"; import "./interface/INFTXEligibility.sol"; import "./interface/INFTXEligibilityManager.sol"; -import "./interface/INFTXSimpleFeeDistributor.sol"; +import "./interface/INFTXFeeDistributor.sol"; import "./token/ERC20FlashMintUpgradeable.sol"; import "./token/ERC721SafeHolderUpgradeable.sol"; import "./token/ERC1155SafeHolderUpgradeable.sol"; @@ -55,7 +55,6 @@ contract NFTXVaultUpgradeable is event VaultShutdown(address assetAddress, uint256 numItems, address recipient); event MetaDataChange(string oldName, string oldSymbol, string newName, string newSymbol); - event FeeSentToDistributor(address user, uint256 amount, uint256 actionType); function __NFTXVault_init( string memory _name, @@ -195,7 +194,7 @@ contract NFTXVaultUpgradeable is // Mint to the user. _mint(to, base * count); uint256 totalFee = mintFee() * count; - _chargeFee(msg.sender, totalFee, 0); + _chargeAndDistributeFees(to, totalFee); emit Minted(tokenIds, amounts, to); return count; @@ -235,7 +234,7 @@ contract NFTXVaultUpgradeable is uint256 totalFee = (_targetRedeemFee * specificIds.length) + ( _randomRedeemFee * (amount - specificIds.length) ); - _chargeFee(msg.sender, totalFee, 1); + _chargeAndDistributeFees(msg.sender, totalFee); // Withdraw from vault. uint256[] memory redeemedIds = withdrawNFTsTo(amount, specificIds, to); @@ -282,7 +281,7 @@ contract NFTXVaultUpgradeable is uint256 totalFee = (_targetSwapFee * specificIds.length) + ( _randomSwapFee * (count - specificIds.length) ); - _chargeFee(msg.sender, totalFee, 3); + _chargeAndDistributeFees(msg.sender, totalFee); // Give the NFTs first, so the user wont get the same thing back, just to be nice. uint256[] memory ids = withdrawNFTsTo(count, specificIds, to); @@ -466,17 +465,22 @@ contract NFTXVaultUpgradeable is return redeemedIds; } - function _chargeFee(address user, uint256 amount, uint256 actionType) internal virtual { + function _chargeAndDistributeFees(address user, uint256 amount) internal virtual { + // Do not charge fees if the zap contract is calling + // Added in v1.0.3. Changed to mapping in v1.0.5. + INFTXVaultFactory _vaultFactory = vaultFactory; if (_vaultFactory.excludedFromFees(msg.sender)) { return; } - + + // Mint fees directly to the distributor and distribute. if (amount > 0) { - INFTXSimpleFeeDistributor feeDistrib = INFTXSimpleFeeDistributor(_vaultFactory.feeDistributor()); - emit FeeSentToDistributor(user, amount, actionType); - _transfer(user, address(feeDistrib), amount); + address feeDistributor = _vaultFactory.feeDistributor(); + // Changed to a _transfer() in v1.0.3. + _transfer(user, feeDistributor, amount); + INFTXFeeDistributor(feeDistributor).distribute(vaultId); } } diff --git a/contracts/solidity/interface/INFTXSimpleFeeDistributor.sol b/contracts/solidity/interface/INFTXSimpleFeeDistributor.sol index 782f00f0..c48b4dba 100644 --- a/contracts/solidity/interface/INFTXSimpleFeeDistributor.sol +++ b/contracts/solidity/interface/INFTXSimpleFeeDistributor.sol @@ -15,9 +15,6 @@ interface INFTXSimpleFeeDistributor { function inventoryStaking() external view returns (address); function treasury() external view returns (address); function allocTotal() external view returns (uint256); - function feeReceiverInfo(uint256 index) external view returns (address, uint256); - // function feeReceiverAddr(uint256 index) external view returns (address); - // function feeReceiverAlloc(uint256 index) external view returns (uint256); // Write functions. function __SimpleFeeDistributor__init__(address _lpStaking, address _treasury) external; diff --git a/hardhat.config.js b/hardhat.config.js index 3684a86b..6da41230 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -22,12 +22,11 @@ module.exports = { goerli: { url: `https://eth-goerli.alchemyapi.io/v2/${process.env.ALCHEMY_GOERLI_API_KEY}`, accounts: [`0x${process.env.DEV_PRIVATE_KEY}`], - gasPrice: 2500000000, + timeout: 1200000, }, mainnet: { url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_MAINNET_API_KEY}`, accounts: [`0x${process.env.DEV_PRIVATE_KEY}`], - timeout: 60000, // gasPrice: 75000000000, }, palm: { diff --git a/scripts/goerli/simple-script.js b/scripts/goerli/simple-script.js deleted file mode 100644 index e187da97..00000000 --- a/scripts/goerli/simple-script.js +++ /dev/null @@ -1,36 +0,0 @@ -const { BigNumber } = require("@ethersproject/bignumber"); -const { ethers, upgrades } = require("hardhat"); -const { utils } = ethers; -const { formatEther, parseEther } = utils; - -async function main() { - const [deployer] = await ethers.getSigners(); - - const myAddress = await deployer.getAddress(); - - console.log("Deploying account:", myAddress); - console.log("Deploying account balance:", (await deployer.getBalance()).toString(), "\n"); - - let factory = await ethers.getContractAt( - "NFTXVaultFactoryUpgradeable", - "0xe01Cf5099e700c282A56E815ABd0C4948298Afae" - ); - - let proxyController = await ethers.getContractAt( - "MultiProxyController", - "0xbde65406B20ADb4ba9D88908187Bc9460fF24da9" - ); - - -} - -main() - .then(() => { - console.log("\nDeployment completed successfully ✓"); - process.exit(0); - }) - .catch((error) => { - console.log("\nDeployment failed ✗"); - console.error(error); - process.exit(1); - }); diff --git a/scripts/goerli/staker-fee-distrib-update.js b/scripts/goerli/staker-fee-distrib-update.js deleted file mode 100644 index d0e3fadc..00000000 --- a/scripts/goerli/staker-fee-distrib-update.js +++ /dev/null @@ -1,72 +0,0 @@ -const { BigNumber } = require("@ethersproject/bignumber"); -const { ethers, upgrades } = require("hardhat"); -const { utils } = ethers; -const { formatEther, parseEther } = utils; - -let factory, proxyController; - -async function main() { - const [deployer] = await ethers.getSigners(); - - const myAddress = await deployer.getAddress(); - - console.log("Deploying account:", myAddress); - console.log("Deploying account balance:", (await deployer.getBalance()).toString(), "\n"); - - factory = await ethers.getContractAt( - "NFTXVaultFactoryUpgradeable", - "0xe01Cf5099e700c282A56E815ABd0C4948298Afae" - ); - - proxyController = await ethers.getContractAt( - "MultiProxyController", - "0xbde65406B20ADb4ba9D88908187Bc9460fF24da9" - ); - - // Upgrade fee distributor - const FeeDistrImpl = await ethers.getContractFactory("NFTXSimpleFeeDistributor"); - const feeDistrImpl = await FeeDistrImpl.deploy(); - await feeDistrImpl.deployed(); - console.log("\nfeeDistrImpl:", feeDistrImpl.address); - - await proxyController.upgradeProxyTo(1, feeDistrImpl.address, {gasLimit: 650000}); - console.log("feeDistr upgraded"); - - // Upgrade LP staking - const LPStakingImpl = await ethers.getContractFactory("NFTXLPStaking"); - const lpStakingImpl = await LPStakingImpl.deploy(); - await lpStakingImpl.deployed(); - console.log("\nlpStakingImpl:", lpStakingImpl.address); - - await proxyController.upgradeProxyTo(2, lpStakingImpl.address, {gasLimit: 650000}); - console.log("lpStaking upgraded"); - - // Upgrade inventory staking - const InvStakingImpl = await ethers.getContractFactory("NFTXInventoryStaking"); - const invStakingImpl = await InvStakingImpl.deploy(); - await invStakingImpl.deployed(); - console.log("\ninvStakingImpl:", invStakingImpl.address); - - await proxyController.upgradeProxyTo(5, invStakingImpl.address, {gasLimit: 650000}); - console.log("invStaking upgraded"); - - // Upgrade vault template - const VaultImpl = await ethers.getContractFactory("NFTXVaultUpgradeable"); - const vaultImpl = await VaultImpl.deploy(); - await vaultImpl.deployed(); - console.log("\nvaultImpl:", vaultImpl.address); - - await factory.upgradeChildTo(vaultImpl.address, {gasLimit: 650000}); - console.log("vaultImpl upgraded"); -} - -main() - .then(() => { - console.log("\nDeployment completed successfully ✓"); - process.exit(0); - }) - .catch((error) => { - console.log("\nDeployment failed ✗"); - console.error(error); - process.exit(1); - }); diff --git a/scripts/simple-script.js b/scripts/simple-script.js index 31a5ab85..02069c5f 100644 --- a/scripts/simple-script.js +++ b/scripts/simple-script.js @@ -11,9 +11,9 @@ async function main() { "\n" ); - const vault = await ethers.getContractAt("NFTXVaultUpgradeable", "0x13edcC775f8895961B81Db5C0205889B3Afb3A16"); + const vault = await ethers.getContractAt("NFTXVaultUpgradeable", "0x0c7f8Cd8262Ecb088B0FB39F4189A07BeCAeacB5"); - await vault.setVaultMetadata("Ringers (VOID)", "RINGER-VOID"); + await vault.setVaultMetadata("rektguy (VOID)", "REKT-VOID"); } diff --git a/test/mainnet/mainnet-fee-distr-update.js b/test/mainnet/mainnet-fee-distr-update.js deleted file mode 100644 index 09ac06e0..00000000 --- a/test/mainnet/mainnet-fee-distr-update.js +++ /dev/null @@ -1,369 +0,0 @@ -const { expect } = require("chai"); -const { expectRevert, expectException } = require("../../utils/expectRevert"); - -const { BigNumber } = require("@ethersproject/bignumber"); -const { ethers, network } = require("hardhat"); -const { utils } = ethers; -const { formatEther, parseEther } = utils; - -const BASE = BigNumber.from(10).pow(18); - -let zetsu, dao, dev; -let factory, proxyController; -let inventoryStaking, lpStaking, stakingZap, feeDistributor; -let paycVault, paycNft, paycVaultId, paycNftIds; - -const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); - -describe("Mainnet unstaking test ERC721", function () { - before("Setup", async () => { - await network.provider.request({ - method: "hardhat_reset", - params: [ - { - forking: { - jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_MAINNET_API_KEY}`, - blockNumber: 15019580, - }, - }, - ], - }); - - await hre.network.provider.request({ - method: "hardhat_impersonateAccount", - params: ["0xc6c2d5ee69745a1e9f2d1a06e0ef0788bd924302"], - }); - await hre.network.provider.request({ - method: "hardhat_impersonateAccount", - params: ["0x40d73df4f99bae688ce3c23a01022224fe16c7b2"], - }); - await hre.network.provider.request({ - method: "hardhat_impersonateAccount", - params: ["0xdea9196dcdd2173d6e369c2acc0facc83fd9346a"], - }); - - zetsu = await ethers.provider.getSigner("0xc6c2d5ee69745a1e9f2d1a06e0ef0788bd924302"); - dao = await ethers.provider.getSigner("0x40d73df4f99bae688ce3c23a01022224fe16c7b2"); - dev = await ethers.provider.getSigner("0xdea9196dcdd2173d6e369c2acc0facc83fd9346a"); - - factory = await ethers.getContractAt( - "NFTXVaultFactoryUpgradeable", - "0xBE86f647b167567525cCAAfcd6f881F1Ee558216" - ); - - proxyController = await ethers.getContractAt( - "MultiProxyController", - "0x35fb4026dcF19f8cA37dcca4D2D68A549548750C" - ); - - paycVault = await ethers.getContractAt("NFTXVaultUpgradeable", "0xa4009D8Eda6F40f549Dfc10f33F56619b9754C90"); - paycNft = await ethers.getContractAt("IERC721Upgradeable", "0x176e0Fe17314DEf59F0F06e976E1b74203be4a55"); - paycVaultId = 305; - paycNftIds = [238, 836, 3831]; - xPayc = await ethers.getContractAt("IERC20Upgradeable", "0x4786d62EdF0DC42D905107f8b04BbC2779aFE90d"); - xPaycWeth = await ethers.getContractAt("IERC20Upgradeable", "0x50414dAa5CFE1fBe65d4b7bDF813f3256b968384"); - - inventoryStaking = await ethers.getContractAt( - "NFTXInventoryStaking", - "0x3E135c3E981fAe3383A5aE0d323860a34CfAB893" - ); - lpStaking = await ethers.getContractAt("NFTXLPStaking", "0x688c3E4658B5367da06fd629E41879beaB538E37"); - stakingZap = await ethers.getContractAt("NFTXStakingZap", "0x7a5e0B4069709cF4D02423b8cafDc608f4436791"); - marketplaceZap = await ethers.getContractAt( - "NFTXMarketplaceZap", - "0x0fc584529a2AEfA997697FAfAcbA5831faC0c22d" - ); - feeDistributor = await ethers.getContractAt( - "INFTXSimpleFeeDistributor", - "0xFD8a76dC204e461dB5da4f38687AdC9CC5ae4a86" - ); - }); - - it("Should lower staking locktimes for tests", async () => { - await inventoryStaking.connect(dao).setInventoryLockTimeErc20(2); - await stakingZap.connect(dev).setLPLockTime(2); - }); - - it("Should upgrade contracts", async () => { - // Upgrade fee distributor - const FeeDistrImpl = await ethers.getContractFactory("NFTXSimpleFeeDistributor"); - const feeDistrImpl = await FeeDistrImpl.deploy(); - await feeDistrImpl.deployed(); - - await proxyController.connect(dao).upgradeProxyTo(1, feeDistrImpl.address); - - // Upgrade LP staking - const LPStakingImpl = await ethers.getContractFactory("NFTXLPStaking"); - const lpStakingImpl = await LPStakingImpl.deploy(); - await lpStakingImpl.deployed(); - - await proxyController.connect(dao).upgradeProxyTo(2, lpStakingImpl.address); - - // Upgrade inventory staking - const InvStakingImpl = await ethers.getContractFactory("NFTXInventoryStaking"); - const invStakingImpl = await InvStakingImpl.deploy(); - await invStakingImpl.deployed(); - - await proxyController.connect(dao).upgradeProxyTo(5, invStakingImpl.address); - - // Upgrade vault (after other upgrades) - const VaultImpl = await ethers.getContractFactory("NFTXVaultUpgradeable"); - const vaultImpl = await VaultImpl.deploy(); - await vaultImpl.deployed(); - - await factory.connect(dao).upgradeChildTo(vaultImpl.address); - }); - - it("Should claim existing LP fees", async () => { - await lpStaking.connect(zetsu).claimRewards(paycVaultId); - }); - - it("Should not distribute fees on mint", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - let zetsuPaycBalA = await paycVault.balanceOf(zetsu._address); - let xPaycShareValA = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalA = await paycVault.balanceOf(xPaycWeth.address); - - await paycVault.connect(zetsu).mint([paycNftIds[0]], [1]); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - let zetsuPaycBalB = await paycVault.balanceOf(zetsu._address); - let xPaycShareValB = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalB = await paycVault.balanceOf(xPaycWeth.address); - - expect(feeDistribPaycBalB).to.equal(feeDistribPaycBalA.add(BASE.mul(10).div(100))); - expect(zetsuPaycBalB).to.equal(zetsuPaycBalA.add(BASE.mul(90).div(100))); - expect(xSlpRewardBalB).to.equal(xSlpRewardBalA); - expect(xPaycShareValB).to.equal(xPaycShareValA); - }); - - it("Should distribute fees on inventory stake", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValA = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalA = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyA = await xPayc.totalSupply(); - - let stakeAmount = BASE.div(2); - await paycVault.connect(zetsu).approve(inventoryStaking.address, BASE.mul(10)); - await inventoryStaking.connect(zetsu).deposit(paycVaultId, stakeAmount); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValB = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalB = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyB = await xPayc.totalSupply(); - - expect(feeDistribPaycBalB.lt(feeDistribPaycBalA)).to.equal(true); - expect(xSlpRewardBalB.gt(xSlpRewardBalA)).to.equal(true); - expect(xPaycShareValB.gt(xPaycShareValA)).to.equal(true); - - let feeDistribPaycBalDif = feeDistribPaycBalA.sub(feeDistribPaycBalB); - let xSlpRewardBalDif = xSlpRewardBalB.sub(xSlpRewardBalA); - expect(xSlpRewardBalDif.gt(feeDistribPaycBalDif.mul(79).div(100))).to.equal(true); - expect(xSlpRewardBalDif.lt(feeDistribPaycBalDif.mul(81).div(100))).to.equal(true); - - let xPaycValA = xPaycSupplyA.mul(xPaycShareValA).div(BASE); - let xPaycValB = xPaycSupplyB.mul(xPaycShareValB).div(BASE); - let xPaycValDif = xPaycValB.sub(xPaycValA); - expect(xPaycValDif.sub(stakeAmount).gt(feeDistribPaycBalDif.mul(19).div(100))).to.equal(true); - expect(xPaycValDif.sub(stakeAmount).lt(feeDistribPaycBalDif.mul(21).div(100))).to.equal(true); - }); - - it("Should not distribute fees on redeem", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - let zetsuPaycBalA = await paycVault.balanceOf(zetsu._address); - let xSlpRewardBalA = await paycVault.balanceOf(xPaycWeth.address); - - await paycVault.connect(zetsu).redeem(1, []); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - let zetsuPaycBalB = await paycVault.balanceOf(zetsu._address); - let xSlpRewardBalB = await paycVault.balanceOf(xPaycWeth.address); - - expect(feeDistribPaycBalB).to.equal(feeDistribPaycBalA.add(BASE.mul(4).div(100))); - expect(zetsuPaycBalB).to.equal(zetsuPaycBalA.sub(BASE.mul(104).div(100))); - expect(xSlpRewardBalB).to.equal(xSlpRewardBalA); - }); - - it("Should distribute fees on liquidity stake", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValA = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalA = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyA = await xPayc.totalSupply(); - - await paycNft.connect(zetsu).setApprovalForAll(stakingZap.address, true); - await stakingZap.connect(zetsu).addLiquidity721ETH(paycVaultId, [paycNftIds[1]], 0, { - value: parseEther("0.05"), - gasLimit: "1000000", - }); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValB = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalB = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyB = await xPayc.totalSupply(); - - expect(feeDistribPaycBalB.lt(feeDistribPaycBalA)).to.equal(true); - expect(xSlpRewardBalB.gt(xSlpRewardBalA)).to.equal(true); - expect(xPaycShareValB.gt(xPaycShareValA)).to.equal(true); - - let feeDistribPaycBalDif = feeDistribPaycBalA.sub(feeDistribPaycBalB); - let xSlpRewardBalDif = xSlpRewardBalB.sub(xSlpRewardBalA); - expect(xSlpRewardBalDif.gt(feeDistribPaycBalDif.mul(79).div(100))).to.equal(true); - expect(xSlpRewardBalDif.lt(feeDistribPaycBalDif.mul(81).div(100))).to.equal(true); - - let xPaycValA = xPaycSupplyA.mul(xPaycShareValA).div(BASE); - let xPaycValB = xPaycSupplyB.mul(xPaycShareValB).div(BASE); - let xPaycValDif = xPaycValB.sub(xPaycValA); - expect(xPaycValDif.gt(feeDistribPaycBalDif.mul(19).div(100))).to.equal(true); - expect(xPaycValDif.lt(feeDistribPaycBalDif.mul(21).div(100))).to.equal(true); - }); - - it("Should not distribute fees on swap", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - let zetsuPaycBalA = await paycVault.balanceOf(zetsu._address); - let xSlpRewardBalA = await paycVault.balanceOf(xPaycWeth.address); - - await paycVault.connect(zetsu).swap([paycNftIds[2]], [1], []); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - let zetsuPaycBalB = await paycVault.balanceOf(zetsu._address); - let xSlpRewardBalB = await paycVault.balanceOf(xPaycWeth.address); - - expect(feeDistribPaycBalB).to.equal(feeDistribPaycBalA.add(BASE.mul(4).div(100))); - expect(zetsuPaycBalB).to.equal(zetsuPaycBalA.sub(BASE.mul(4).div(100))); - expect(xSlpRewardBalB).to.equal(xSlpRewardBalA); - }); - - it("Should distribute fees on inventory unstake", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValA = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalA = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyA = await xPayc.totalSupply(); - - let xTokenShareValueA = await inventoryStaking.xTokenShareValue(paycVaultId); - let adjustedXTokenShareValueA = await inventoryStaking.adjustedXTokenShareValue(paycVaultId); - expect(adjustedXTokenShareValueA.gt(xTokenShareValueA)).to.equal(true); - - let unstakeAmount = BASE.div(10); - let unstakeAmountUnderlying = unstakeAmount.mul(xTokenShareValueA).div(BASE); - await sleep(3000); - await inventoryStaking.connect(zetsu).withdraw(paycVaultId, unstakeAmount); - - let xTokenShareValueB = await inventoryStaking.adjustedXTokenShareValue(paycVaultId); - expect(adjustedXTokenShareValueA).to.equal(xTokenShareValueB); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValB = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalB = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyB = await xPayc.totalSupply(); - - expect(feeDistribPaycBalB.lt(feeDistribPaycBalA)).to.equal(true); - expect(xSlpRewardBalB.gt(xSlpRewardBalA)).to.equal(true); - expect(xPaycShareValB.gt(xPaycShareValA)).to.equal(true); - - let feeDistribPaycBalDif = feeDistribPaycBalA.sub(feeDistribPaycBalB); - let xSlpRewardBalDif = xSlpRewardBalB.sub(xSlpRewardBalA); - expect(xSlpRewardBalDif.gt(feeDistribPaycBalDif.mul(79).div(100))).to.equal(true); - expect(xSlpRewardBalDif.lt(feeDistribPaycBalDif.mul(81).div(100))).to.equal(true); - - let xPaycValA = xPaycSupplyA.mul(xPaycShareValA).div(BASE); - let xPaycValB = xPaycSupplyB.mul(xPaycShareValB).div(BASE); - let xPaycValDif = xPaycValB.sub(xPaycValA); - expect(xPaycValDif.add(unstakeAmountUnderlying).gt(feeDistribPaycBalDif.mul(19).div(100))).to.equal(true); - expect(xPaycValDif.add(unstakeAmountUnderlying).lt(feeDistribPaycBalDif.mul(21).div(100))).to.equal(true); - }); - - it("Should redeem again to accrue fees in distributor", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - - await paycVault.connect(zetsu).redeem(1, []); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - - expect(feeDistribPaycBalB).to.equal(feeDistribPaycBalA.add(BASE.mul(4).div(100))); - }); - - it("Should distribute fees on liquidity rewards claim", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValA = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalA = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyA = await xPayc.totalSupply(); - - let zetsuPaycBalA = await paycVault.balanceOf(zetsu._address); - let adjustedDividendOfZetsuA = await lpStaking.adjustedDividendOf(paycVaultId, zetsu._address); - - await lpStaking.connect(zetsu).claimRewards(paycVaultId); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValB = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalB = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyB = await xPayc.totalSupply(); - - let zetsuPaycBalB = await paycVault.balanceOf(zetsu._address); - let zetsuPaycBalDif = zetsuPaycBalB.sub(zetsuPaycBalA); - expect(zetsuPaycBalDif).to.equal(adjustedDividendOfZetsuA); - - expect(feeDistribPaycBalB.lt(feeDistribPaycBalA)).to.equal(true); - expect(xSlpRewardBalB.gt(xSlpRewardBalA)).to.equal(true); - expect(xPaycShareValB.gt(xPaycShareValA)).to.equal(true); - - let feeDistribPaycBalDif = feeDistribPaycBalA.sub(feeDistribPaycBalB); - let xSlpRewardBalDif = xSlpRewardBalB.sub(xSlpRewardBalA); - expect(xSlpRewardBalDif.add(zetsuPaycBalDif).gt(feeDistribPaycBalDif.mul(79).div(100))).to.equal(true); - expect(xSlpRewardBalDif.add(zetsuPaycBalDif).lt(feeDistribPaycBalDif.mul(81).div(100))).to.equal(true); - - let xPaycValA = xPaycSupplyA.mul(xPaycShareValA).div(BASE); - let xPaycValB = xPaycSupplyB.mul(xPaycShareValB).div(BASE); - let xPaycValDif = xPaycValB.sub(xPaycValA); - expect(xPaycValDif.gt(feeDistribPaycBalDif.mul(19).div(100))).to.equal(true); - expect(xPaycValDif.lt(feeDistribPaycBalDif.mul(21).div(100))).to.equal(true); - }); - - it("Should redeem again to accrue fees in distributor", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - - await paycVault.connect(zetsu).redeem(1, []); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - - expect(feeDistribPaycBalB).to.equal(feeDistribPaycBalA.add(BASE.mul(4).div(100))); - }); - - it("Should distribute fees on liquidity unstake", async () => { - let feeDistribPaycBalA = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValA = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalA = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyA = await xPayc.totalSupply(); - - let zetsuPaycBalA = await paycVault.balanceOf(zetsu._address); - let adjustedDividendOfZetsuA = await lpStaking.adjustedDividendOf(paycVaultId, zetsu._address); - - let xPaycWethBal = await xPaycWeth.balanceOf(zetsu._address); - await sleep(3000); - await lpStaking.connect(zetsu).withdraw(paycVaultId, xPaycWethBal); - - let feeDistribPaycBalB = await paycVault.balanceOf(feeDistributor.address); - let xPaycShareValB = await inventoryStaking.xTokenShareValue(paycVaultId); - let xSlpRewardBalB = await paycVault.balanceOf(xPaycWeth.address); - let xPaycSupplyB = await xPayc.totalSupply(); - - let zetsuPaycBalB = await paycVault.balanceOf(zetsu._address); - let zetsuPaycBalDif = zetsuPaycBalB.sub(zetsuPaycBalA); - expect(zetsuPaycBalDif.sub(1).lte(adjustedDividendOfZetsuA)).to.equal(true); - expect(zetsuPaycBalDif.add(1).gte(adjustedDividendOfZetsuA)).to.equal(true); - - expect(feeDistribPaycBalB.lt(feeDistribPaycBalA)).to.equal(true); - expect(xSlpRewardBalB.gt(xSlpRewardBalA)).to.equal(true); - expect(xPaycShareValB.gt(xPaycShareValA)).to.equal(true); - - let feeDistribPaycBalDif = feeDistribPaycBalA.sub(feeDistribPaycBalB); - let xSlpRewardBalDif = xSlpRewardBalB.sub(xSlpRewardBalA); - expect(xSlpRewardBalDif.add(zetsuPaycBalDif).gt(feeDistribPaycBalDif.mul(79).div(100))).to.equal(true); - expect(xSlpRewardBalDif.add(zetsuPaycBalDif).lt(feeDistribPaycBalDif.mul(81).div(100))).to.equal(true); - - let xPaycValA = xPaycSupplyA.mul(xPaycShareValA).div(BASE); - let xPaycValB = xPaycSupplyB.mul(xPaycShareValB).div(BASE); - let xPaycValDif = xPaycValB.sub(xPaycValA); - expect(xPaycValDif.gt(feeDistribPaycBalDif.mul(19).div(100))).to.equal(true); - expect(xPaycValDif.lt(feeDistribPaycBalDif.mul(21).div(100))).to.equal(true); - }); -});