From 0e174bbee1106619a727596a35d235ef53f14a91 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 14:30:03 +0530 Subject: [PATCH 01/15] VaultCreationZap: fix token authorization --- contracts/solidity/zaps/VaultCreationZap.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/solidity/zaps/VaultCreationZap.sol b/contracts/solidity/zaps/VaultCreationZap.sol index 7adaf20..fc516df 100644 --- a/contracts/solidity/zaps/VaultCreationZap.sol +++ b/contracts/solidity/zaps/VaultCreationZap.sol @@ -24,6 +24,7 @@ interface IWETH { function transfer(address to, uint value) external returns (bool); function withdraw(uint) external; function balanceOf(address to) external view returns (uint256); + function approve(address guy, uint wad) external returns (bool); } @@ -117,6 +118,8 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155SafeHolderUpgr // Set our chain's WETH contract WETH = IWETH(_weth); + // setting infinite approval here to save on subsequent gas costs + WETH.approve(_sushiRouter, type(uint256).max); } @@ -231,6 +234,7 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155SafeHolderUpgr address(this), block.timestamp ); + IERC20Upgradeable(baseToken).safeApprove(address(sushiRouter), 0); // Stake in LP rewards contract address lpToken = sushiHelper.pairFor(sushiRouter.factory(), baseToken, address(WETH)); From 93ad784dedbafbd53c5526195d9f2a2104c878cf Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 14:50:00 +0530 Subject: [PATCH 02/15] VaultCreationZap: fix CryptoPunks transfer authorization --- contracts/solidity/zaps/VaultCreationZap.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contracts/solidity/zaps/VaultCreationZap.sol b/contracts/solidity/zaps/VaultCreationZap.sol index fc516df..abb3e95 100644 --- a/contracts/solidity/zaps/VaultCreationZap.sol +++ b/contracts/solidity/zaps/VaultCreationZap.sol @@ -183,6 +183,15 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155SafeHolderUpgr // Iterate over our 721 tokens to transfer them all to our vault for (uint i; i < length;) { _transferFromERC721(vaultData.assetAddress, assetTokens.assetTokenIds[i], address(vault)); + bytes memory data = abi.encodeWithSignature( + "offerPunkForSaleToAddress(uint256,uint256,address)", + assetTokens.assetTokenIds[i], + 0, + address(vault) + ); + (bool success, bytes memory resultData) = vaultData.assetAddress.call(data); + require(success, string(resultData)); + unchecked { ++i; } } } else { From 44cc2fa51d8ca062e7c5eb4a9f86f10e728c86c9 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 14:53:43 +0530 Subject: [PATCH 03/15] NFTXMarketplace0xZap: remove incorrect require check --- contracts/solidity/NFTXMarketplace0xZap.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/contracts/solidity/NFTXMarketplace0xZap.sol b/contracts/solidity/NFTXMarketplace0xZap.sol index 549814d..49ddd79 100644 --- a/contracts/solidity/NFTXMarketplace0xZap.sol +++ b/contracts/solidity/NFTXMarketplace0xZap.sol @@ -218,9 +218,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab // Check that we have an amount specified require(amount > 0, 'Must send amount'); - // Check that we have a message value sent - require(msg.value >= amount, 'Invalid amount'); - // Wrap ETH into WETH for our contract from the sender WETH.deposit{value: msg.value}(); From f814d3577b5873a00d9b5a56f59670121abcb844 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 14:56:41 +0530 Subject: [PATCH 04/15] NFTXMarketplace0xZap: add check for quoteAmount received --- contracts/solidity/NFTXMarketplace0xZap.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/solidity/NFTXMarketplace0xZap.sol b/contracts/solidity/NFTXMarketplace0xZap.sol index 49ddd79..5369c4e 100644 --- a/contracts/solidity/NFTXMarketplace0xZap.sol +++ b/contracts/solidity/NFTXMarketplace0xZap.sol @@ -227,6 +227,9 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab // Buy vault tokens that will cover our transaction uint256 quoteAmount = _fillQuote(address(WETH), vault, swapCallData); + // check if received sufficient vault tokens + require(quoteAmount >= amount * 1e18, 'Insufficient vault tokens'); + // Redeem token IDs from the vault _redeem(vaultId, amount, specificIds, to); emit Buy(amount, quoteAmount, to); From 85b0aa2de10f7222ef24f39f9514648d8f2f9d33 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 15:09:35 +0530 Subject: [PATCH 05/15] NFTXMarketplace0xZap: remove spender, fix _transferDust call --- contracts/solidity/NFTXMarketplace0xZap.sol | 32 +++++++-------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/contracts/solidity/NFTXMarketplace0xZap.sol b/contracts/solidity/NFTXMarketplace0xZap.sol index 5369c4e..2efb4c4 100644 --- a/contracts/solidity/NFTXMarketplace0xZap.sol +++ b/contracts/solidity/NFTXMarketplace0xZap.sol @@ -111,7 +111,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab * * @param vaultId The ID of the NFTX vault * @param ids An array of token IDs to be minted - * @param spender The `allowanceTarget` field from the API response * @param swapCallData The `data` field from the API response * @param to The recipient of the WETH from the tx */ @@ -119,7 +118,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab function mintAndSell721( uint256 vaultId, uint256[] calldata ids, - address spender, bytes calldata swapCallData, address payable to ) external nonReentrant onlyOwnerIfPaused { @@ -138,8 +136,8 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab // Emit our sale event emit Sell(ids.length, amount, to); - // Transfer dust back to the spender - _transferDust(spender, vault); + // Transfer dust back to the sender + _transferDust(msg.sender, vault); } @@ -151,7 +149,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab * @param vaultId The ID of the NFTX vault * @param idsIn An array of random token IDs to be minted * @param specificIds An array of any specific token IDs to be minted - * @param spender The `allowanceTarget` field from the API response * @param swapCallData The `data` field from the API response * @param to The recipient of the WETH from the tx */ @@ -159,8 +156,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab function buyAndSwap721( uint256 vaultId, uint256[] calldata idsIn, - uint256[] calldata specificIds, - address spender, + uint256[] calldata specificIds, bytes calldata swapCallData, address payable to ) external payable nonReentrant onlyOwnerIfPaused { @@ -186,8 +182,8 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab _swap721(vaultId, idsIn, specificIds, to); emit Swap(idsIn.length, amount, to); - // Transfer dust back to the spender - _transferDust(spender, vault); + // Transfer dust back to the sender + _transferDust(msg.sender, vault); } @@ -199,7 +195,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab * @param vaultId The ID of the NFTX vault * @param amount The number of tokens to buy * @param specificIds An array of any specific token IDs to be minted - * @param spender The `allowanceTarget` field from the API response * @param swapCallData The `data` field from the API response * @param to The recipient of the WETH from the tx */ @@ -208,7 +203,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab uint256 vaultId, uint256 amount, uint256[] calldata specificIds, - address spender, bytes calldata swapCallData, address payable to ) external payable nonReentrant onlyOwnerIfPaused { @@ -234,8 +228,8 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab _redeem(vaultId, amount, specificIds, to); emit Buy(amount, quoteAmount, to); - // Transfer dust back to the spender - _transferDust(spender, vault); + // Transfer dust back to the sender + _transferDust(msg.sender, vault); } @@ -245,7 +239,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab * @param vaultId The ID of the NFTX vault * @param ids An array of token IDs to be minted * @param amounts The number of the corresponding ID to be minted - * @param spender The `allowanceTarget` field from the API response * @param swapCallData The `data` field from the API response * @param to The recipient of the WETH from the tx */ @@ -254,7 +247,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab uint256 vaultId, uint256[] calldata ids, uint256[] calldata amounts, - address spender, bytes calldata swapCallData, address payable to ) external nonReentrant onlyOwnerIfPaused { @@ -274,8 +266,8 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab // Emit our sale event emit Sell(totalAmount, amount, to); - // Transfer dust back to the spender - _transferDust(spender, vault); + // Transfer dust back to the sender + _transferDust(msg.sender, vault); } @@ -287,7 +279,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab * @param vaultId The ID of the NFTX vault * @param idsIn An array of random token IDs to be minted * @param specificIds An array of any specific token IDs to be minted - * @param spender The `allowanceTarget` field from the API response * @param swapCallData The `data` field from the API response * @param to The recipient of the WETH from the tx */ @@ -297,7 +288,6 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab uint256[] calldata idsIn, uint256[] calldata amounts, uint256[] calldata specificIds, - address spender, bytes calldata swapCallData, address payable to ) external payable nonReentrant onlyOwnerIfPaused { @@ -324,8 +314,8 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab _swap1155(vaultId, idsIn, amounts, specificIds, to); emit Swap(totalAmount, amount, to); - // Transfer dust back to the spender - _transferDust(spender, vault); + // Transfer dust back to the sender + _transferDust(msg.sender, vault); } From 11c11ed4d34cfbc5c40dd85ac048b86bf2223956 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 15:14:02 +0530 Subject: [PATCH 06/15] NFTXYieldStakingZap: add check for remaining WETH amount --- contracts/solidity/NFTXYieldStakingZap.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contracts/solidity/NFTXYieldStakingZap.sol b/contracts/solidity/NFTXYieldStakingZap.sol index a4a16ee..336b6d0 100644 --- a/contracts/solidity/NFTXYieldStakingZap.sol +++ b/contracts/solidity/NFTXYieldStakingZap.sol @@ -20,6 +20,7 @@ interface IWETH { function transfer(address to, uint value) external returns (bool); function withdraw(uint) external; function balanceOf(address to) external view returns (uint256); + function approve(address guy, uint wad) external returns (bool); } @@ -181,6 +182,10 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { uint256 vaultTokenAmount = _fillQuote(baseToken, swapCallData); require(vaultTokenAmount > minTokenIn, 'Insufficient tokens acquired'); + // Check WETH balance + uint256 WETHAmount = WETH.balanceOf(address(this)) - wethBalance; + require(WETHAmount >= wethIn, 'Insufficient WETH remaining'); + // Provide liquidity to sushiswap, using the vault token that we acquired from 0x and // pairing it with the liquidity amount specified in the call. IERC20Upgradeable(baseToken).safeApprove(address(sushiRouter), minTokenIn); From 8209711c85718f67763fe5bb6a8986b7a18967f4 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 15:16:53 +0530 Subject: [PATCH 07/15] NFTXYieldStakingZap: fix addLiquidity call & token approvals --- contracts/solidity/NFTXYieldStakingZap.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/solidity/NFTXYieldStakingZap.sol b/contracts/solidity/NFTXYieldStakingZap.sol index 336b6d0..a42ad70 100644 --- a/contracts/solidity/NFTXYieldStakingZap.sol +++ b/contracts/solidity/NFTXYieldStakingZap.sol @@ -84,6 +84,8 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { // Set our chain's WETH contract WETH = IWETH(_weth); + // setting infinite approval here to save on subsequent gas costs + WETH.approve(_sushiRouter, type(uint256).max); // Set our 0x Swap Target swapTarget = _swapTarget; @@ -188,11 +190,11 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { // Provide liquidity to sushiswap, using the vault token that we acquired from 0x and // pairing it with the liquidity amount specified in the call. - IERC20Upgradeable(baseToken).safeApprove(address(sushiRouter), minTokenIn); + IERC20Upgradeable(baseToken).safeApprove(address(sushiRouter), vaultTokenAmount); (uint256 amountToken, , uint256 liquidity) = sushiRouter.addLiquidity( baseToken, address(WETH), - minTokenIn, + vaultTokenAmount, wethIn, minTokenIn, minWethIn, From dde43e6cd37ae659948fe92e316eba4575e7081a Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 15:18:42 +0530 Subject: [PATCH 08/15] NFTXYieldStakingZap: one-time WETH approval to swapTarget --- contracts/solidity/NFTXYieldStakingZap.sol | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/contracts/solidity/NFTXYieldStakingZap.sol b/contracts/solidity/NFTXYieldStakingZap.sol index a42ad70..7b65e7b 100644 --- a/contracts/solidity/NFTXYieldStakingZap.sol +++ b/contracts/solidity/NFTXYieldStakingZap.sol @@ -86,6 +86,7 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { WETH = IWETH(_weth); // setting infinite approval here to save on subsequent gas costs WETH.approve(_sushiRouter, type(uint256).max); + WETH.approve(_swapTarget, type(uint256).max); // Set our 0x Swap Target swapTarget = _swapTarget; @@ -284,11 +285,6 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { // Track our balance of the buyToken to determine how much we've bought. uint256 boughtAmount = IERC20Upgradeable(buyToken).balanceOf(address(this)); - // Give `swapTarget` an infinite allowance to spend this contract's `sellToken`. - // Note that for some tokens (e.g., USDT, KNC), you must first reset any existing - // allowance to 0 before being able to update it. - require(IERC20Upgradeable(address(WETH)).approve(swapTarget, type(uint256).max), 'Unable to approve contract'); - // Call the encoded swap function call on the contract at `swapTarget` (bool success,) = swapTarget.call(swapCallData); require(success, 'SWAP_CALL_FAILED'); From 731a6bcb6c45a98d5141685983b0c986f3647c55 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 15:21:36 +0530 Subject: [PATCH 09/15] NFTXYieldStakingZap: add onlyOwnerIfPaused modifier --- contracts/solidity/NFTXYieldStakingZap.sol | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/contracts/solidity/NFTXYieldStakingZap.sol b/contracts/solidity/NFTXYieldStakingZap.sol index 7b65e7b..39e53c4 100644 --- a/contracts/solidity/NFTXYieldStakingZap.sol +++ b/contracts/solidity/NFTXYieldStakingZap.sol @@ -105,7 +105,7 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { function buyAndStakeInventory( uint256 vaultId, bytes calldata swapCallData - ) external payable nonReentrant { + ) external payable nonReentrant onlyOwnerIfPaused { // Ensure we have tx value require(msg.value > 0, 'Invalid value provided'); @@ -164,7 +164,7 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { uint256 minWethIn, uint256 wethIn - ) external payable nonReentrant { + ) external payable nonReentrant onlyOwnerIfPaused { // Ensure we have tx value require(msg.value > 0, 'Invalid value provided'); require(msg.value > wethIn, 'Insufficient vault sent for pairing'); @@ -321,6 +321,17 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { paused = _paused; } + /** + * @notice A modifier that only allows the owner to interact with the function + * if the contract is paused. If the contract is not paused then anyone can + * interact with the function. + */ + + modifier onlyOwnerIfPaused() { + require(!paused || msg.sender == owner(), "Zap is paused"); + _; + } + /** * @notice Allows our contract to only receive WETH and reject everything else. From d2acdde85bad68285cd2b87d7463ec25a9ba3f28 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 15:51:33 +0530 Subject: [PATCH 10/15] remove upgradeable contract imports --- contracts/solidity/NFTXMarketplace0xZap.sol | 32 ++-- contracts/solidity/NFTXYieldStakingZap.sol | 18 +-- contracts/solidity/testing/ERC1155Holder.sol | 36 +++++ contracts/solidity/testing/ERC721Holder.sol | 28 ++++ contracts/solidity/testing/IERC20.sol | 89 +++++++++++ contracts/solidity/testing/IERC20Permit.sol | 60 ++++++++ contracts/solidity/util/SafeERC20.sol | 151 +++++++++++++++++++ contracts/solidity/zaps/VaultCreationZap.sol | 26 ++-- 8 files changed, 399 insertions(+), 41 deletions(-) create mode 100644 contracts/solidity/testing/ERC1155Holder.sol create mode 100644 contracts/solidity/testing/ERC721Holder.sol create mode 100644 contracts/solidity/testing/IERC20.sol create mode 100644 contracts/solidity/testing/IERC20Permit.sol create mode 100644 contracts/solidity/util/SafeERC20.sol diff --git a/contracts/solidity/NFTXMarketplace0xZap.sol b/contracts/solidity/NFTXMarketplace0xZap.sol index 2efb4c4..6ebb83b 100644 --- a/contracts/solidity/NFTXMarketplace0xZap.sol +++ b/contracts/solidity/NFTXMarketplace0xZap.sol @@ -4,24 +4,18 @@ pragma solidity ^0.8.0; import "./interface/INFTXVault.sol"; import "./interface/INFTXVaultFactory.sol"; -import "./token/IERC1155Upgradeable.sol"; -import "./token/ERC721HolderUpgradeable.sol"; -import "./token/ERC1155HolderUpgradeable.sol"; +import "./testing/IERC1155.sol"; +import "./testing/ERC721Holder.sol"; +import "./testing/ERC1155Holder.sol"; import "./util/Ownable.sol"; import "./util/ReentrancyGuard.sol"; -import "./util/SafeERC20Upgradeable.sol"; +import "./util/SafeERC20.sol"; /** * @notice A partial ERC20 interface. */ -interface IERC20 { - function balanceOf(address owner) external view returns (uint256); - function approve(address spender, uint256 amount) external returns (bool); - function transfer(address to, uint256 amount) external returns (bool); -} - /** * @notice A partial WETH interface. @@ -43,9 +37,9 @@ interface IWETH { * @author Twade */ -contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeable, ERC1155HolderUpgradeable { +contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155Holder { - using SafeERC20Upgradeable for IERC20Upgradeable; + using SafeERC20 for IERC20; /// @notice Allows zap to be paused bool public paused = false; @@ -362,8 +356,8 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab // Transfer tokens from the message sender to the vault address assetAddress = INFTXVault(vault).assetAddress(); - IERC1155Upgradeable(assetAddress).safeBatchTransferFrom(msg.sender, address(this), ids, amounts, ""); - IERC1155Upgradeable(assetAddress).setApprovalForAll(vault, true); + IERC1155(assetAddress).safeBatchTransferFrom(msg.sender, address(this), ids, amounts, ""); + IERC1155(assetAddress).setApprovalForAll(vault, true); // Mint our tokens from the vault to this contract INFTXVault(vault).mint(ids, amounts); @@ -436,8 +430,8 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab // Transfer tokens to zap and mint to NFTX. address assetAddress = INFTXVault(vault).assetAddress(); - IERC1155Upgradeable(assetAddress).safeBatchTransferFrom(msg.sender, address(this), idsIn, amounts, ""); - IERC1155Upgradeable(assetAddress).setApprovalForAll(vault, true); + IERC1155(assetAddress).safeBatchTransferFrom(msg.sender, address(this), idsIn, amounts, ""); + IERC1155(assetAddress).setApprovalForAll(vault, true); INFTXVault(vault).swapTo(idsIn, amounts, idsOut, to); return vault; @@ -555,9 +549,9 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab require(success, "Unable to send unwrapped WETH"); } - uint dustBalance = IERC20Upgradeable(vault).balanceOf(address(this)); + uint dustBalance = IERC20(vault).balanceOf(address(this)); if (dustBalance > 0) { - IERC20Upgradeable(vault).transfer(spender, dustBalance); + IERC20(vault).transfer(spender, dustBalance); } emit DustReturned(remaining, dustBalance, spender); @@ -638,7 +632,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721HolderUpgradeab (bool success, ) = payable(msg.sender).call{value: address(this).balance}(""); require(success, "Address: unable to send value"); } else { - IERC20Upgradeable(token).safeTransfer(msg.sender, IERC20Upgradeable(token).balanceOf(address(this))); + IERC20(token).safeTransfer(msg.sender, IERC20(token).balanceOf(address(this))); } } diff --git a/contracts/solidity/NFTXYieldStakingZap.sol b/contracts/solidity/NFTXYieldStakingZap.sol index 39e53c4..5c3223a 100644 --- a/contracts/solidity/NFTXYieldStakingZap.sol +++ b/contracts/solidity/NFTXYieldStakingZap.sol @@ -8,7 +8,7 @@ import "./interface/INFTXVaultFactory.sol"; import "./interface/IUniswapV2Router01.sol"; import "./util/Ownable.sol"; import "./util/ReentrancyGuard.sol"; -import "./util/SafeERC20Upgradeable.sol"; +import "./util/SafeERC20.sol"; /** @@ -33,7 +33,7 @@ interface IWETH { contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { - using SafeERC20Upgradeable for IERC20Upgradeable; + using SafeERC20 for IERC20; /// @notice Allows zap to be paused bool public paused = false; @@ -126,7 +126,7 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { // Make a direct timelock mint using the default timelock duration. This sends directly // to our user, rather than via the zap, to avoid the timelock locking the tx. - IERC20Upgradeable(baseToken).transfer(inventoryStaking.vaultXToken(vaultId), vaultTokenAmount); + IERC20(baseToken).transfer(inventoryStaking.vaultXToken(vaultId), vaultTokenAmount); inventoryStaking.timelockMintFor(vaultId, vaultTokenAmount, msg.sender, 2); // Return any left of WETH to the user as ETH @@ -191,7 +191,7 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { // Provide liquidity to sushiswap, using the vault token that we acquired from 0x and // pairing it with the liquidity amount specified in the call. - IERC20Upgradeable(baseToken).safeApprove(address(sushiRouter), vaultTokenAmount); + IERC20(baseToken).safeApprove(address(sushiRouter), vaultTokenAmount); (uint256 amountToken, , uint256 liquidity) = sushiRouter.addLiquidity( baseToken, address(WETH), @@ -205,13 +205,13 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { // Stake in LP rewards contract address lpToken = pairFor(baseToken, address(WETH)); - IERC20Upgradeable(lpToken).safeApprove(address(lpStaking), liquidity); + IERC20(lpToken).safeApprove(address(lpStaking), liquidity); lpStaking.timelockDepositFor(vaultId, msg.sender, liquidity, 48 hours); // Return any token dust to the caller uint256 remainingTokens = vaultTokenAmount - amountToken; if (remainingTokens != 0) { - IERC20Upgradeable(baseToken).transfer(msg.sender, remainingTokens); + IERC20(baseToken).transfer(msg.sender, remainingTokens); } // Return any left of WETH to the user as ETH @@ -266,7 +266,7 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { (bool success, ) = payable(msg.sender).call{value: address(this).balance}(""); require(success, "Address: unable to send value"); } else { - IERC20Upgradeable(token).safeTransfer(msg.sender, IERC20Upgradeable(token).balanceOf(address(this))); + IERC20(token).safeTransfer(msg.sender, IERC20(token).balanceOf(address(this))); } } @@ -283,14 +283,14 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { bytes calldata swapCallData ) internal returns (uint256) { // Track our balance of the buyToken to determine how much we've bought. - uint256 boughtAmount = IERC20Upgradeable(buyToken).balanceOf(address(this)); + uint256 boughtAmount = IERC20(buyToken).balanceOf(address(this)); // Call the encoded swap function call on the contract at `swapTarget` (bool success,) = swapTarget.call(swapCallData); require(success, 'SWAP_CALL_FAILED'); // Use our current buyToken balance to determine how much we've bought. - return IERC20Upgradeable(buyToken).balanceOf(address(this)) - boughtAmount; + return IERC20(buyToken).balanceOf(address(this)) - boughtAmount; } diff --git a/contracts/solidity/testing/ERC1155Holder.sol b/contracts/solidity/testing/ERC1155Holder.sol new file mode 100644 index 0000000..7249de8 --- /dev/null +++ b/contracts/solidity/testing/ERC1155Holder.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol) + +pragma solidity ^0.8.0; + +import "./ERC1155Receiver.sol"; + +/** + * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens. + * + * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be + * stuck. + * + * @dev _Available since v3.1._ + */ +contract ERC1155Holder is ERC1155Receiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] memory, + uint256[] memory, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } +} diff --git a/contracts/solidity/testing/ERC721Holder.sol b/contracts/solidity/testing/ERC721Holder.sol new file mode 100644 index 0000000..1093853 --- /dev/null +++ b/contracts/solidity/testing/ERC721Holder.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol) + +pragma solidity ^0.8.0; + +import "./IERC721Receiver.sol"; + +/** + * @dev Implementation of the {IERC721Receiver} interface. + * + * Accepts all token transfers. + * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. + */ +contract ERC721Holder is IERC721Receiver { + /** + * @dev See {IERC721Receiver-onERC721Received}. + * + * Always returns `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received( + address, + address, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC721Received.selector; + } +} diff --git a/contracts/solidity/testing/IERC20.sol b/contracts/solidity/testing/IERC20.sol new file mode 100644 index 0000000..82a00e4 --- /dev/null +++ b/contracts/solidity/testing/IERC20.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) + external + view + returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} diff --git a/contracts/solidity/testing/IERC20Permit.sol b/contracts/solidity/testing/IERC20Permit.sol new file mode 100644 index 0000000..bb43e53 --- /dev/null +++ b/contracts/solidity/testing/IERC20Permit.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + */ +interface IERC20Permit { + /** + * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, + * given ``owner``'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {nonces}). + * + * For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP + * section]. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases ``owner``'s nonce by one. This + * prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); + + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/contracts/solidity/util/SafeERC20.sol b/contracts/solidity/util/SafeERC20.sol new file mode 100644 index 0000000..7e99227 --- /dev/null +++ b/contracts/solidity/util/SafeERC20.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) + +pragma solidity ^0.8.0; + +import "../testing/IERC20.sol"; +import "../testing/IERC20Permit.sol"; +import "./Address.sol"; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using Address for address; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + _callOptionalReturn( + token, + abi.encodeWithSelector(token.transfer.selector, to, value) + ); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + _callOptionalReturn( + token, + abi.encodeWithSelector(token.transferFrom.selector, from, to, value) + ); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn( + token, + abi.encodeWithSelector(token.approve.selector, spender, value) + ); + } + + function safeIncreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + uint256 newAllowance = token.allowance(address(this), spender) + value; + _callOptionalReturn( + token, + abi.encodeWithSelector( + token.approve.selector, + spender, + newAllowance + ) + ); + } + + function safeDecreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + unchecked { + uint256 oldAllowance = token.allowance(address(this), spender); + require( + oldAllowance >= value, + "SafeERC20: decreased allowance below zero" + ); + uint256 newAllowance = oldAllowance - value; + _callOptionalReturn( + token, + abi.encodeWithSelector( + token.approve.selector, + spender, + newAllowance + ) + ); + } + } + + function safePermit( + IERC20Permit token, + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + uint256 nonceBefore = token.nonces(owner); + token.permit(owner, spender, value, deadline, v, r, s); + uint256 nonceAfter = token.nonces(owner); + require( + nonceAfter == nonceBefore + 1, + "SafeERC20: permit did not succeed" + ); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall( + data, + "SafeERC20: low-level call failed" + ); + if (returndata.length > 0) { + // Return data is optional + require( + abi.decode(returndata, (bool)), + "SafeERC20: ERC20 operation did not succeed" + ); + } + } +} diff --git a/contracts/solidity/zaps/VaultCreationZap.sol b/contracts/solidity/zaps/VaultCreationZap.sol index abb3e95..a7e9d0c 100644 --- a/contracts/solidity/zaps/VaultCreationZap.sol +++ b/contracts/solidity/zaps/VaultCreationZap.sol @@ -7,11 +7,11 @@ import "../interface/INFTXLPStaking.sol"; import "../interface/IUniswapV2Router01.sol"; import "../interface/INFTXVault.sol"; import "../interface/INFTXVaultFactory.sol"; -import "../token/IERC1155Upgradeable.sol"; -import "../token/ERC1155SafeHolderUpgradeable.sol"; +import "../testing/IERC1155.sol"; +import "../testing/ERC1155Holder.sol"; import "../util/Ownable.sol"; import "../util/ReentrancyGuard.sol"; -import "../util/SafeERC20Upgradeable.sol"; +import "../util/SafeERC20.sol"; import "../util/SushiHelper.sol"; @@ -35,9 +35,9 @@ interface IWETH { * @author Twade */ -contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155SafeHolderUpgradeable { +contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155Holder { - using SafeERC20Upgradeable for IERC20Upgradeable; + using SafeERC20 for IERC20; /// @notice Allows zap to be paused bool public paused = false; @@ -197,7 +197,7 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155SafeHolderUpgr } else { // Transfer all of our 1155 tokens to our zap, as the `mintTo` call on our // vault requires the call sender to hold the ERC1155 token. - IERC1155Upgradeable(vaultData.assetAddress).safeBatchTransferFrom( + IERC1155(vaultData.assetAddress).safeBatchTransferFrom( msg.sender, address(this), assetTokens.assetTokenIds, @@ -206,7 +206,7 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155SafeHolderUpgr ); // Approve our vault to play with our 1155 tokens - IERC1155Upgradeable(vaultData.assetAddress).setApprovalForAll(address(vault), true); + IERC1155(vaultData.assetAddress).setApprovalForAll(address(vault), true); } // We can now mint our asset tokens, giving the vault our tokens and storing them @@ -228,11 +228,11 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155SafeHolderUpgr WETH.deposit{value: msg.value}(); // Convert WETH to vault token - require(IERC20Upgradeable(baseToken).balanceOf(address(this)) >= assetTokens.minTokenIn, 'Insufficient tokens acquired for liquidity'); + require(IERC20(baseToken).balanceOf(address(this)) >= assetTokens.minTokenIn, 'Insufficient tokens acquired for liquidity'); // Provide liquidity to sushiswap, using the vault tokens and pairing it with the // liquidity amount specified in the call. - IERC20Upgradeable(baseToken).safeApprove(address(sushiRouter), assetTokens.minTokenIn); + IERC20(baseToken).safeApprove(address(sushiRouter), assetTokens.minTokenIn); (,, uint256 liquidity) = sushiRouter.addLiquidity( baseToken, address(WETH), @@ -243,23 +243,23 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155SafeHolderUpgr address(this), block.timestamp ); - IERC20Upgradeable(baseToken).safeApprove(address(sushiRouter), 0); + IERC20(baseToken).safeApprove(address(sushiRouter), 0); // Stake in LP rewards contract address lpToken = sushiHelper.pairFor(sushiRouter.factory(), baseToken, address(WETH)); - IERC20Upgradeable(lpToken).safeApprove(address(lpStaking), liquidity); + IERC20(lpToken).safeApprove(address(lpStaking), liquidity); lpStaking.timelockDepositFor(vaultId_, msg.sender, liquidity, 48 hours); } // Return any token dust to the caller - uint256 remainingTokens = IERC20Upgradeable(baseToken).balanceOf(address(this)); + uint256 remainingTokens = IERC20(baseToken).balanceOf(address(this)); // Any tokens that we have remaining after our liquidity staking are thrown into // inventory to ensure what we don't have any token dust remaining. if (remainingTokens > 0) { // Make a direct timelock mint using the default timelock duration. This sends directly // to our user, rather than via the zap, to avoid the timelock locking the tx. - IERC20Upgradeable(baseToken).transfer(inventoryStaking.vaultXToken(vaultId_), remainingTokens); + IERC20(baseToken).transfer(inventoryStaking.vaultXToken(vaultId_), remainingTokens); inventoryStaking.timelockMintFor(vaultId_, remainingTokens, msg.sender, 2); } } From 9352fa4e84867de9a44dd4e86f428c578916d25a Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 15:53:06 +0530 Subject: [PATCH 11/15] fix calling immutable vars in constructor --- contracts/solidity/NFTXYieldStakingZap.sol | 4 ++-- contracts/solidity/zaps/VaultCreationZap.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/solidity/NFTXYieldStakingZap.sol b/contracts/solidity/NFTXYieldStakingZap.sol index 5c3223a..c36a2e3 100644 --- a/contracts/solidity/NFTXYieldStakingZap.sol +++ b/contracts/solidity/NFTXYieldStakingZap.sol @@ -85,8 +85,8 @@ contract NFTXYieldStakingZap is Ownable, ReentrancyGuard { // Set our chain's WETH contract WETH = IWETH(_weth); // setting infinite approval here to save on subsequent gas costs - WETH.approve(_sushiRouter, type(uint256).max); - WETH.approve(_swapTarget, type(uint256).max); + IWETH(_weth).approve(_sushiRouter, type(uint256).max); + IWETH(_weth).approve(_swapTarget, type(uint256).max); // Set our 0x Swap Target swapTarget = _swapTarget; diff --git a/contracts/solidity/zaps/VaultCreationZap.sol b/contracts/solidity/zaps/VaultCreationZap.sol index a7e9d0c..aa60da1 100644 --- a/contracts/solidity/zaps/VaultCreationZap.sol +++ b/contracts/solidity/zaps/VaultCreationZap.sol @@ -119,7 +119,7 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155Holder { // Set our chain's WETH contract WETH = IWETH(_weth); // setting infinite approval here to save on subsequent gas costs - WETH.approve(_sushiRouter, type(uint256).max); + IWETH(_weth).approve(_sushiRouter, type(uint256).max); } From 40a8e8df70233ee1152724ff3885c4820bc86fa1 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Tue, 29 Nov 2022 21:58:02 +0530 Subject: [PATCH 12/15] fix punk authorization call --- contracts/solidity/zaps/VaultCreationZap.sol | 24 ++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/contracts/solidity/zaps/VaultCreationZap.sol b/contracts/solidity/zaps/VaultCreationZap.sol index aa60da1..aa277a1 100644 --- a/contracts/solidity/zaps/VaultCreationZap.sol +++ b/contracts/solidity/zaps/VaultCreationZap.sol @@ -56,6 +56,9 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155Holder { INFTXInventoryStaking public immutable inventoryStaking; INFTXLPStaking public immutable lpStaking; + // Set a constant address for specific contracts that need special logic + address constant CRYPTO_PUNKS = 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB; + /// @notice Basic information pertaining to the vault struct vaultInfo { address assetAddress; // 20/32 @@ -183,14 +186,17 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155Holder { // Iterate over our 721 tokens to transfer them all to our vault for (uint i; i < length;) { _transferFromERC721(vaultData.assetAddress, assetTokens.assetTokenIds[i], address(vault)); - bytes memory data = abi.encodeWithSignature( - "offerPunkForSaleToAddress(uint256,uint256,address)", - assetTokens.assetTokenIds[i], - 0, - address(vault) - ); - (bool success, bytes memory resultData) = vaultData.assetAddress.call(data); - require(success, string(resultData)); + + if(vaultData.assetAddress == CRYPTO_PUNKS) { + bytes memory data = abi.encodeWithSignature( + "offerPunkForSaleToAddress(uint256,uint256,address)", + assetTokens.assetTokenIds[i], + 0, + address(vault) + ); + (bool success, bytes memory resultData) = vaultData.assetAddress.call(data); + require(success, string(resultData)); + } unchecked { ++i; } } @@ -310,7 +316,7 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155Holder { function _transferFromERC721(address assetAddr, uint256 tokenId, address to) internal virtual { bytes memory data; - if (assetAddr == 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB) { + if (assetAddr == CRYPTO_PUNKS) { // Fix here for frontrun attack. bytes memory punkIndexToAddress = abi.encodeWithSignature("punkIndexToAddress(uint256)", tokenId); (bool checkSuccess, bytes memory result) = address(assetAddr).staticcall(punkIndexToAddress); From 09abe1b48a034c356dff75628f65a70129bc13c2 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Wed, 21 Dec 2022 19:04:44 +0530 Subject: [PATCH 13/15] NFTXMarketplace0xZap: fix to param --- contracts/solidity/NFTXMarketplace0xZap.sol | 56 +++++++++++++-------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/contracts/solidity/NFTXMarketplace0xZap.sol b/contracts/solidity/NFTXMarketplace0xZap.sol index 6ebb83b..25b6e51 100644 --- a/contracts/solidity/NFTXMarketplace0xZap.sol +++ b/contracts/solidity/NFTXMarketplace0xZap.sol @@ -106,7 +106,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 * @param vaultId The ID of the NFTX vault * @param ids An array of token IDs to be minted * @param swapCallData The `data` field from the API response - * @param to The recipient of the WETH from the tx + * @param to The recipient of ETH from the tx */ function mintAndSell721( @@ -127,11 +127,14 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 // Sell our vault token for WETH uint256 amount = _fillQuote(vault, address(WETH), swapCallData); + // convert WETH to ETH and send to `to` + _transferAllWETH(to); + // Emit our sale event emit Sell(ids.length, amount, to); // Transfer dust back to the sender - _transferDust(msg.sender, vault); + _transferDust(msg.sender, vault, false); } @@ -144,7 +147,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 * @param idsIn An array of random token IDs to be minted * @param specificIds An array of any specific token IDs to be minted * @param swapCallData The `data` field from the API response - * @param to The recipient of the WETH from the tx + * @param to The recipient of the token IDs from the tx */ function buyAndSwap721( @@ -177,7 +180,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 emit Swap(idsIn.length, amount, to); // Transfer dust back to the sender - _transferDust(msg.sender, vault); + _transferDust(msg.sender, vault, true); } @@ -190,7 +193,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 * @param amount The number of tokens to buy * @param specificIds An array of any specific token IDs to be minted * @param swapCallData The `data` field from the API response - * @param to The recipient of the WETH from the tx + * @param to The recipient of the token IDs from the tx */ function buyAndRedeem( @@ -223,7 +226,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 emit Buy(amount, quoteAmount, to); // Transfer dust back to the sender - _transferDust(msg.sender, vault); + _transferDust(msg.sender, vault, true); } @@ -234,7 +237,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 * @param ids An array of token IDs to be minted * @param amounts The number of the corresponding ID to be minted * @param swapCallData The `data` field from the API response - * @param to The recipient of the WETH from the tx + * @param to The recipient of ETH from the tx */ function mintAndSell1155( @@ -257,11 +260,14 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 // Sell our vault token for WETH uint256 amount = _fillQuote(vault, address(WETH), swapCallData); + // convert WETH to ETH and send to `to` + _transferAllWETH(to); + // Emit our sale event emit Sell(totalAmount, amount, to); // Transfer dust back to the sender - _transferDust(msg.sender, vault); + _transferDust(msg.sender, vault, false); } @@ -274,7 +280,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 * @param idsIn An array of random token IDs to be minted * @param specificIds An array of any specific token IDs to be minted * @param swapCallData The `data` field from the API response - * @param to The recipient of the WETH from the tx + * @param to The recipient of ETH from the tx */ function buyAndSwap1155( @@ -309,7 +315,7 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 emit Swap(totalAmount, amount, to); // Transfer dust back to the sender - _transferDust(msg.sender, vault); + _transferDust(msg.sender, vault, true); } @@ -536,25 +542,33 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 /** * @notice Transfers remaining ETH and vault token dust to a recipient. * - * @param spender Address of the dust recipient + * @param recipient Address of the dust recipient * @param vault Address of the vault token + * @param isWETHDust Checks and transfers WETH dust if boolean is true */ - function _transferDust(address spender, address vault) internal { - uint256 remaining = WETH.balanceOf(address(this)); - if (remaining > 0) { - // Unwrap our WETH into ETH and transfer it to the recipient - WETH.withdraw(remaining); - (bool success, ) = payable(spender).call{value: remaining}(""); - require(success, "Unable to send unwrapped WETH"); + function _transferDust(address recipient, address vault, bool isWETHDust) internal { + uint256 remaining; + if(isWETHDust) { + remaining = _transferAllWETH(recipient); } uint dustBalance = IERC20(vault).balanceOf(address(this)); if (dustBalance > 0) { - IERC20(vault).transfer(spender, dustBalance); + IERC20(vault).transfer(recipient, dustBalance); } - emit DustReturned(remaining, dustBalance, spender); + emit DustReturned(remaining, dustBalance, recipient); + } + + function _transferAllWETH(address recipient) internal returns(uint256 amount) { + amount = WETH.balanceOf(address(this)); + if (amount > 0) { + // Unwrap our WETH into ETH and transfer it to the recipient + WETH.withdraw(amount); + (bool success, ) = payable(recipient).call{value: amount}(""); + require(success, "Unable to send unwrapped WETH"); + } } @@ -655,4 +669,4 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 receive() external payable {} -} +} \ No newline at end of file From 987f490e6cc12d550ebd6539d6dcb99061468830 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Thu, 22 Dec 2022 21:16:34 +0530 Subject: [PATCH 14/15] buyAndSwap1155: fix comments --- contracts/solidity/NFTXMarketplace0xZap.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/solidity/NFTXMarketplace0xZap.sol b/contracts/solidity/NFTXMarketplace0xZap.sol index 25b6e51..13054ea 100644 --- a/contracts/solidity/NFTXMarketplace0xZap.sol +++ b/contracts/solidity/NFTXMarketplace0xZap.sol @@ -274,13 +274,13 @@ contract NFTXMarketplace0xZap is Ownable, ReentrancyGuard, ERC721Holder, ERC1155 /** * @notice Purchases vault tokens from 0x with WETH and then swaps the tokens for * either random or specific token IDs from the vault. The specified recipient will - * receive the ERC721 tokens, as well as any WETH dust that is left over from the tx. + * receive the ERC1155 tokens, as well as any WETH dust that is left over from the tx. * * @param vaultId The ID of the NFTX vault * @param idsIn An array of random token IDs to be minted * @param specificIds An array of any specific token IDs to be minted * @param swapCallData The `data` field from the API response - * @param to The recipient of ETH from the tx + * @param to The recipient of token IDs from the tx */ function buyAndSwap1155( From a5da756ca70bc863db09d309fb237a63ca64fdca Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Thu, 22 Dec 2022 21:18:03 +0530 Subject: [PATCH 15/15] VaultCreationZap: fix equality check --- contracts/solidity/zaps/VaultCreationZap.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/solidity/zaps/VaultCreationZap.sol b/contracts/solidity/zaps/VaultCreationZap.sol index aa277a1..f434394 100644 --- a/contracts/solidity/zaps/VaultCreationZap.sol +++ b/contracts/solidity/zaps/VaultCreationZap.sol @@ -228,7 +228,7 @@ contract NFTXVaultCreationZap is Ownable, ReentrancyGuard, ERC1155Holder { // We first want to set up our liquidity, as the returned values will be variable if (assetTokens.minTokenIn > 0) { - require(msg.value > assetTokens.wethIn, 'Insufficient vault sent for liquidity'); + require(msg.value >= assetTokens.wethIn, 'Insufficient msg.value sent for liquidity'); // Wrap ETH into WETH for our contract from the sender WETH.deposit{value: msg.value}();