Skip to content
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

Vault creation zap #19

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions contracts/solidity/testing/WETH.sol
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;
}
}
198 changes: 198 additions & 0 deletions contracts/solidity/zaps/VaultCreationZap.sol
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,
tomwade marked this conversation as resolved.
Show resolved Hide resolved
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.
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be 0 to indicate a module... maybe you can handle this some other way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that makes sense, can check >= and allow negative numbers passed I reckon, will check it out

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);
}

}
Loading