-
Notifications
You must be signed in to change notification settings - Fork 47
Vault creation zap #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 10 commits
2dbfeee
759eb16
3f0f31d
d40a421
4510387
7965c3c
52b6994
3e974f0
027b182
c922830
1646d4c
9d91b51
7f1b9a0
0e1d587
5209596
afd9b0f
5f43e57
e994d1d
37bb8c2
1bec606
c63f8c1
0c2d15f
8606b13
adfbc2d
438e4c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
pragma solidity ^0.8.0; | ||
|
||
contract WETH { | ||
string public name = "Wrapped Ether"; | ||
string public symbol = "WETH"; | ||
uint8 public decimals = 18; | ||
|
||
event Approval(address indexed src, address indexed guy, uint wad); | ||
event Transfer(address indexed src, address indexed dst, uint wad); | ||
event Deposit(address indexed dst, uint wad); | ||
event Withdrawal(address indexed src, uint wad); | ||
|
||
mapping (address => uint) public balanceOf; | ||
mapping (address => mapping (address => uint)) public allowance; | ||
|
||
receive() external payable { | ||
deposit(); | ||
} | ||
|
||
function mint(address to, uint256 amount) public { | ||
balanceOf[to] += amount; | ||
} | ||
|
||
function deposit() public payable { | ||
balanceOf[msg.sender] += msg.value; | ||
emit Deposit(msg.sender, msg.value); | ||
} | ||
|
||
function withdraw(uint wad) public payable { | ||
require(balanceOf[msg.sender] >= wad); | ||
balanceOf[msg.sender] -= wad; | ||
payable(msg.sender).transfer(wad); | ||
emit Withdrawal(msg.sender, wad); | ||
} | ||
|
||
function totalSupply() public view returns (uint) { | ||
return address(this).balance; | ||
} | ||
|
||
function approve(address guy, uint wad) public returns (bool) { | ||
allowance[msg.sender][guy] = wad; | ||
emit Approval(msg.sender, guy, wad); | ||
return true; | ||
} | ||
|
||
function transfer(address dst, uint wad) public returns (bool) { | ||
return transferFrom(msg.sender, dst, wad); | ||
} | ||
|
||
function transferFrom(address src, address dst, uint wad) | ||
public | ||
returns (bool) | ||
{ | ||
require(balanceOf[src] >= wad); | ||
|
||
if (src != msg.sender && allowance[src][msg.sender] >= 0) { | ||
require(allowance[src][msg.sender] >= wad); | ||
allowance[src][msg.sender] -= wad; | ||
} | ||
|
||
balanceOf[src] -= wad; | ||
balanceOf[dst] += wad; | ||
|
||
emit Transfer(src, dst, wad); | ||
|
||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "../interface/INFTXVault.sol"; | ||
import "../interface/INFTXVaultFactory.sol"; | ||
import "../token/IERC1155Upgradeable.sol"; | ||
import "../util/ReentrancyGuardUpgradeable.sol"; | ||
|
||
|
||
/** | ||
* @notice An amalgomation of vault creation steps, merged and optimised in | ||
* a single contract call in an attempt reduce gas costs to the end-user. | ||
* | ||
* @author Twade | ||
*/ | ||
|
||
contract NFTXVaultCreationZap is ReentrancyGuardUpgradeable { | ||
|
||
/// @notice An interface for the NFTX Vault Factory contract | ||
INFTXVaultFactory public immutable vaultFactory; | ||
|
||
/// @notice Basic information pertaining to the vault | ||
struct vaultInfo { | ||
address assetAddress; // 20/32 | ||
bool is1155; // 21/32 | ||
bool allowAllItems; // 22/32 | ||
string name; // ??/32 | ||
string symbol; // ??/32 | ||
} | ||
|
||
/// @notice Fee information in 9-decimal format | ||
struct vaultFeesConfig { | ||
uint32 mintFee; | ||
uint32 randomRedeemFee; | ||
uint32 targetRedeemFee; | ||
uint32 randomSwapFee; | ||
uint32 targetSwapFee; | ||
} | ||
|
||
/// @notice Reference to the vault's eligibility implementation | ||
struct vaultEligibilityStorage { | ||
uint moduleIndex; | ||
bytes initData; | ||
} | ||
|
||
/// @notice Valid tokens to be transferred to the vault on creation | ||
struct vaultTokens { | ||
uint[] assetTokenIds; | ||
uint[] assetTokenAmounts; | ||
} | ||
|
||
|
||
/** | ||
* @notice Initialises our zap by setting contract addresses onto their | ||
* respective interfaces. | ||
* | ||
* @param _vaultFactory NFTX Vault Factory contract address | ||
*/ | ||
|
||
constructor(address _vaultFactory) { | ||
vaultFactory = INFTXVaultFactory(_vaultFactory); | ||
} | ||
|
||
|
||
/** | ||
* @notice Creates an NFTX vault, handling any desired settings and tokens. | ||
* | ||
* @dev Tokens are deposited into the vault prior to fees being sent. | ||
* | ||
* @param vaultData Basic information about the vault stored in `vaultInfo` struct | ||
* @param vaultFeatures A numeric representation of boolean values for features on the vault | ||
* @param vaultFees Fee definitions stored in a `vaultFeesConfig` struct | ||
* @param eligibilityStorage Eligibility implementation, stored in a `vaultEligibilityStorage` struct | ||
* @param assetTokens Tokens to be transferred to the vault in exchange for vault tokens | ||
* | ||
* @return vaultId_ The numeric ID of the NFTX vault | ||
*/ | ||
|
||
function createVault( | ||
vaultInfo calldata vaultData, | ||
uint vaultFeatures, | ||
vaultFeesConfig calldata vaultFees, | ||
vaultEligibilityStorage calldata eligibilityStorage, | ||
vaultTokens calldata assetTokens | ||
) external nonReentrant returns (uint vaultId_) { | ||
// Create our vault skeleton | ||
vaultId_ = vaultFactory.createVault( | ||
vaultData.name, | ||
vaultData.symbol, | ||
vaultData.assetAddress, | ||
vaultData.is1155, | ||
vaultData.allowAllItems | ||
); | ||
|
||
// Build our vault interface | ||
INFTXVault vault = INFTXVault(vaultFactory.vault(vaultId_)); | ||
|
||
// If we have specified vault features then update them | ||
if (vaultFeatures > 0) { | ||
vault.setVaultFeatures( | ||
_getBoolean(vaultFeatures, 0), | ||
_getBoolean(vaultFeatures, 1), | ||
_getBoolean(vaultFeatures, 2), | ||
_getBoolean(vaultFeatures, 3), | ||
_getBoolean(vaultFeatures, 4) | ||
); | ||
} | ||
|
||
// Mint and stake liquidity into the vault | ||
uint length = assetTokens.assetTokenIds.length; | ||
if (length > 0) { | ||
if (!vaultData.is1155) { | ||
for (uint i; i < length;) { | ||
_transferFromERC721(vaultData.assetAddress, assetTokens.assetTokenIds[i], address(vault)); | ||
unchecked { ++i; } | ||
} | ||
} else { | ||
IERC1155Upgradeable(vaultData.assetAddress).safeBatchTransferFrom( | ||
msg.sender, | ||
address(this), | ||
assetTokens.assetTokenIds, | ||
assetTokens.assetTokenAmounts, | ||
"" | ||
); | ||
} | ||
|
||
// We can now mint our asset tokens, giving the vault our tokens | ||
vault.mintTo(assetTokens.assetTokenIds, assetTokens.assetTokenAmounts, msg.sender); | ||
tomwade marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
} | ||
|
||
// Set our vault fees, converting our 9-decimal to 18-decimal | ||
vault.setFees( | ||
uint256(vaultFees.mintFee) * 10e9, | ||
uint256(vaultFees.randomRedeemFee) * 10e9, | ||
uint256(vaultFees.targetRedeemFee) * 10e9, | ||
uint256(vaultFees.randomSwapFee) * 10e9, | ||
uint256(vaultFees.targetSwapFee) * 10e9 | ||
); | ||
|
||
// If we have a specified eligibility storage, add that on | ||
if (eligibilityStorage.moduleIndex > 0) { | ||
|
||
vault.deployEligibilityStorage( | ||
eligibilityStorage.moduleIndex, | ||
eligibilityStorage.initData | ||
); | ||
} | ||
|
||
// Finalise our vault, preventing further edits | ||
vault.finalizeVault(); | ||
} | ||
|
||
|
||
/** | ||
* @notice Transfers our ERC721 tokens to a specified recipient. | ||
* | ||
* @param assetAddr Address of the asset being transferred | ||
* @param tokenId The ID of the token being transferred | ||
* @param to The address the token is being transferred to | ||
*/ | ||
|
||
function _transferFromERC721(address assetAddr, uint256 tokenId, address to) internal virtual { | ||
bytes memory data; | ||
|
||
if (assetAddr == 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB) { | ||
// Fix here for frontrun attack. | ||
bytes memory punkIndexToAddress = abi.encodeWithSignature("punkIndexToAddress(uint256)", tokenId); | ||
(bool checkSuccess, bytes memory result) = address(assetAddr).staticcall(punkIndexToAddress); | ||
(address nftOwner) = abi.decode(result, (address)); | ||
require(checkSuccess && nftOwner == msg.sender, "Not the NFT owner"); | ||
data = abi.encodeWithSignature("buyPunk(uint256)", tokenId); | ||
} else { | ||
// We push to the vault to avoid an unneeded transfer. | ||
data = abi.encodeWithSignature("safeTransferFrom(address,address,uint256)", msg.sender, to, tokenId); | ||
} | ||
|
||
(bool success, bytes memory resultData) = address(assetAddr).call(data); | ||
require(success, string(resultData)); | ||
} | ||
|
||
|
||
/** | ||
* @notice Reads a boolean at a set character index of a uint. | ||
* | ||
* @dev 0 and 1 define false and true respectively. | ||
* | ||
* @param _packedBools A numeric representation of a series of boolean values | ||
* @param _boolNumber The character index of the boolean we are looking up | ||
* | ||
* @return bool The representation of the boolean value | ||
*/ | ||
|
||
function _getBoolean(uint256 _packedBools, uint256 _boolNumber) internal pure returns(bool) { | ||
uint256 flag = (_packedBools >> _boolNumber) & uint256(1); | ||
return (flag == 1 ? true : false); | ||
} | ||
|
||
} |
Uh oh!
There was an error while loading. Please reload this page.