Vault

Vault2 Contract Documentation

The Vault2 contract is a secure smart contract designed for storing and managing ERC20 tokens and ERC721 NFTs. It provides functionalities such as depositing assets, setting withdrawal limits, multi-signature transaction approvals, and a recovery mechanism. The contract is upgradeable through a proxy pattern and is intended to be used in conjunction with the VaultFactory2 and SimpleProxy contracts.


Overview

The Vault2 contract serves as a secure vault for storing and managing ERC20 tokens and ERC721 NFTs. It allows the owner to deposit assets, set customizable withdrawal limits, and require multiple confirmations for transactions exceeding daily limits. The contract also includes a recovery address and a freeze mechanism to enhance security in case of compromised access.


Key Components

Interfaces and Libraries

  • IERC20: Interface for interacting with ERC20 tokens.

  • IERC721: Interface for interacting with ERC721 NFTs.

  • IERC721Receiver: Interface for contracts that can handle safe transfers of ERC721 tokens.

Data Structures

Transaction

solidityCopy codestruct Transaction {
    address to;
    bytes data;
    uint256 timestamp;
    bool executed;
    uint256 confirmations;
    uint256 amount;
}
  • to: The recipient address of the transaction.

  • data: The call data for the transaction.

  • timestamp: The time when the transaction was queued.

  • executed: Indicates if the transaction has been executed.

  • confirmations: The number of confirmations received.

  • amount: The Ether amount to be transferred.

TokenLimit

solidityCopy codestruct TokenLimit {
    uint256 fixedLimit;
    uint256 percentageLimit;
    uint256 useBaseLimit;
}
  • fixedLimit: A fixed amount limit for withdrawals; takes precedence over percentage limits if set.

  • percentageLimit: A withdrawal limit based on a percentage of the total balance.

  • useBaseLimit:

    • 0: No limit.

    • 1: Use base daily limit.

    • 2: Limit is zero (withdrawals disallowed).

Events

  • TokenDeposited

    Emitted when tokens are deposited into the vault.

    solidityCopy codeevent TokenDeposited(
        address token,
        uint256 amount,
        address indexed depositor
    );
  • NFTDeposited

    Emitted when an NFT is deposited into the vault.

    solidityCopy codeevent NFTDeposited(
        address nft,
        uint256 tokenId,
        address indexed depositor
    );
  • TokenWithdrawn

    Emitted when tokens are withdrawn from the vault.

    solidityCopy codeevent TokenWithdrawn(address token, uint256 amount);
  • TransactionQueued

    Emitted when a transaction is queued for later execution.

    solidityCopy codeevent TransactionQueued(
        uint256 txIndex,
        address to,
        bytes data,
        uint256 amount
    );
  • TransactionExecuted

    Emitted when a queued transaction is executed.

    solidityCopy codeevent TransactionExecuted(uint256 txIndex, address to, bytes data);
  • TransactionConfirmed

    Emitted when a transaction is confirmed by an authorized address.

    solidityCopy codeevent TransactionConfirmed(uint256 txIndex, address confirmer);

Contract Variables

Ownership and Access Control

  • address public owner: The owner of the vault.

  • string public name: The name of the vault.

  • address public recoveryAddress: The address designated for recovery operations.

  • address[] public whitelistedAddresses: Addresses authorized to perform certain actions.

  • mapping(address => bool) public isWhitelisted: Mapping to check if an address is whitelisted.

Limits and Thresholds

  • uint256 public dailyLimit: The base daily withdrawal limit (in percentage).

  • uint256 public threshold: The number of confirmations required to execute a queued transaction.

  • uint256 public delay: The time delay (in seconds) before a transaction can be executed.

  • mapping(address => TokenLimit) public tokenLimits: Specific withdrawal limits for individual tokens.

Transactions and State

  • Transaction[] public queuedTransactions: An array of queued transactions awaiting execution.

  • uint public queuedTxs: The total number of queued transactions.

  • uint public freeze: Indicator for the freeze status of the vault (e.g., 0 for active, higher values for different freeze levels).

  • uint public version: The version of the vault contract.

  • mapping(uint256 => mapping(address => bool)) public confirmed: Tracks confirmations for each transaction by address.

Withdrawal Tracking

  • mapping(address => uint256) public dailyWithdrawnAmount: Tracks the amount withdrawn per token in the current day.

  • mapping(address => uint256) public lastWithdrawTimestamp: Records the timestamp of the last withdrawal per token.


Modifiers

  • onlyOwner

    Restricts the function to be called only by the owner.

    solidityCopy codemodifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }
  • onlyWhitelisted

    Restricts the function to be called only by whitelisted addresses.

    solidityCopy codemodifier onlyWhitelisted() {
        require(isWhitelisted[msg.sender], "Not a whitelisted address");
        _;
    }
  • onlyRecoveryAddress

    Restricts the function to be called only by the recovery address.

    solidityCopy codemodifier onlyRecoveryAddress() {
        require(msg.sender == recoveryAddress, "Not the recovery address");
        _;
    }

Functions

Initialization

init

Initializes the vault with the necessary parameters. This function must be called only once.

solidityCopy codefunction init(
    address _owner,
    string memory _name,
    address _recoveryAddress,
    address[] memory _whitelistedAddresses,
    uint256 _dailyLimit,
    uint256 _threshold,
    uint256 _delay
) public
  • Parameters:

    • _owner: The address of the vault owner.

    • _name: The name of the vault.

    • _recoveryAddress: The address designated for recovery operations.

    • _whitelistedAddresses: An array of addresses to be whitelisted.

    • _dailyLimit: The base daily withdrawal limit (in percentage).

    • _threshold: The number of confirmations required for transaction execution.

    • _delay: The delay before a transaction can be executed.

  • Requirements:

    • The function can only be called once (owner must be address(0) before initialization).

Deposit Functions

depositToken

Allows depositing ERC20 tokens or Ether into the vault.

solidityCopy codefunction depositToken(address token, uint256 amount) public payable
  • Parameters:

    • token: The address of the ERC20 token contract. Use address(0) for Ether.

    • amount: The amount of tokens or Ether to deposit.

  • Logic:

    • For Ether (token == address(0)), ensures the sent value matches the amount.

    • For ERC20 tokens, transfers the amount from the sender to the vault.

  • Emits:

    • TokenDeposited

depositNFT

Allows depositing an ERC721 NFT into the vault.

solidityCopy codefunction depositNFT(address nft, uint256 tokenId) public
  • Parameters:

    • nft: The address of the ERC721 contract.

    • tokenId: The ID of the NFT token to deposit.

  • Logic:

    • Uses safeTransferFrom to transfer the NFT to the vault.

  • Emits:

    • NFTDeposited

Withdrawal Functions

withdrawToken

Allows the owner to withdraw tokens or Ether from the vault, subject to withdrawal limits and freeze status.

solidityCopy codefunction withdrawToken(
    address to,
    address token,
    uint256 amount
) public onlyOwner
  • Parameters:

    • to: The recipient address.

    • token: The address of the ERC20 token contract. Use address(0) for Ether.

    • amount: The amount to withdraw.

  • Logic:

    • Checks if the vault has sufficient balance.

    • Verifies that the vault is not frozen (freeze == 0).

    • Calculates the remaining withdrawal limit.

    • If the amount exceeds the remaining limit, queues the withdrawal.

    • Otherwise, updates the withdrawal tracking and executes the withdrawal.

  • Emits:

    • TokenWithdrawn (if withdrawal is executed immediately).

    • TransactionQueued (if withdrawal is queued).

executeWithdrawal

Executes an immediate withdrawal of tokens or Ether.

solidityCopy codefunction executeWithdrawal(
    address to,
    address token,
    uint256 amount
) internal
  • Parameters:

    • to: The recipient address.

    • token: The address of the ERC20 token contract. Use address(0) for Ether.

    • amount: The amount to withdraw.

  • Logic:

    • Transfers the specified amount to the recipient.

    • Emits TokenWithdrawn.

    • Creates and executes a placeholder transaction for record-keeping.

Transaction Management

queueTransaction

Queues a transaction to be executed later, subject to confirmations and delays.

solidityCopy codefunction queueTransaction(
    address to,
    bytes memory data,
    uint256 amount
) public onlyOwner
  • Parameters:

    • to: The address to which the transaction is directed.

    • data: The call data for the transaction.

    • amount: The amount of Ether to transfer (if any).

  • Logic:

    • Verifies that the vault is not frozen.

    • Adds a new transaction to the queuedTransactions array.

    • Increments queuedTxs.

    • Emits TransactionQueued.

confirmTransaction

Allows the owner or whitelisted addresses to confirm a queued transaction.

solidityCopy codefunction confirmTransaction(uint256 txIndex) public
  • Parameters:

    • txIndex: The index of the transaction in the queuedTransactions array.

  • Logic:

    • Checks if the caller is authorized.

    • Verifies the transaction exists and is not already executed.

    • Records the confirmation.

    • If the confirmation threshold is met, executes the transaction.

  • Emits:

    • TransactionConfirmed

    • TransactionExecuted (if executed)

executeTransaction

Executes a queued transaction that has met the required confirmations.

solidityCopy codefunction executeTransaction(uint256 txIndex) internal
  • Parameters:

    • txIndex: The index of the transaction in the queuedTransactions array.

  • Logic:

    • Ensures the transaction has enough confirmations and is not executed.

    • Performs the call to the target address with the specified data and amount.

    • Marks the transaction as executed.

    • Emits TransactionExecuted.

cancelTransaction

Allows the owner or recovery address to cancel a queued transaction.

solidityCopy codefunction cancelTransaction(uint256 txIndex) public
  • Parameters:

    • txIndex: The index of the transaction in the queuedTransactions array.

  • Logic:

    • Checks if the caller is authorized.

    • Verifies the transaction exists and is not already executed.

    • Marks the transaction as executed and sets confirmations to 404 to indicate cancellation.

Settings and Configuration

setTokenLimit

Sets specific withdrawal limits for a token.

solidityCopy codefunction setTokenLimit(
    address token,
    uint256 fixedLimit,
    uint256 percentageLimit,
    uint256 useBaseLimit
) external
  • Parameters:

    • token: The address of the ERC20 token contract.

    • fixedLimit: A fixed withdrawal limit amount.

    • percentageLimit: A withdrawal limit based on a percentage of the total balance.

    • useBaseLimit: Indicator for base limit usage (0, 1, or 2).

  • Requirements:

    • Can only be called by the contract itself.

updateSettings

Allows updating multiple settings at once, such as the recovery address, whitelisted addresses, daily limits, etc.

solidityCopy codefunction updateSettings(
    address newRecoveryAddress,
    address[] memory newWhitelistedAddresses,
    uint256 newDailyLimit,
    uint256 newThreshold,
    uint256 newDelay,
    address[] memory tokens,
    uint256[] memory fixedLimits,
    uint256[] memory percentageLimits,
    uint256[] memory useBaseLimits
) external
  • Parameters:

    • newRecoveryAddress: The new recovery address.

    • newWhitelistedAddresses: An array of new whitelisted addresses.

    • newDailyLimit: The new base daily withdrawal limit.

    • newThreshold: The new confirmation threshold.

    • newDelay: The new execution delay.

    • tokens: An array of token addresses for which limits are being set.

    • fixedLimits: Corresponding fixed limits for the tokens.

    • percentageLimits: Corresponding percentage limits for the tokens.

    • useBaseLimits: Indicators for base limit usage for the tokens.

  • Requirements:

    • Can only be called by the contract itself or the recovery address.

Recovery and Freeze Mechanism

recover

Allows the recovery address to recover tokens or Ether from the vault and set the freeze status.

solidityCopy codefunction recover(
    address token,
    address to,
    uint256 amount,
    bytes memory data,
    uint freezeL
) external onlyRecoveryAddress
  • Parameters:

    • token: The address of the ERC20 token contract. Use address(0) for Ether.

    • to: The recipient address.

    • amount: The amount to recover.

    • data: Additional data for the transaction.

    • freezeL: The new freeze level to set.

  • Logic:

    • Transfers the specified amount to the recipient.

    • Updates the freeze status.

    • Emits TokenWithdrawn.

freezeLock

Allows authorized addresses to set the freeze status of the vault.

solidityCopy codefunction freezeLock(uint freezeL) external
  • Parameters:

    • freezeL: The new freeze level to set.

  • Requirements:

    • Can be called by the contract itself, the owner, the recovery address, or whitelisted addresses.

    • If called by someone other than the recovery address, the freeze level can only be increased.

Utility Functions

getLimitAmount

Calculates the current withdrawal limit amount for a token.

solidityCopy codefunction getLimitAmount(address token) public view returns (uint256)
  • Parameters:

    • token: The address of the ERC20 token contract.

  • Returns:

    • The calculated limit amount based on token-specific or base limits.

getLimit

Determines the allowable immediate withdrawal amount based on limits and previous withdrawals.

solidityCopy codefunction getLimit(
    address to,
    address token,
    uint256 amount
) public view returns (uint)
  • Parameters:

    • to: The recipient address.

    • token: The address of the ERC20 token contract.

    • amount: The desired withdrawal amount.

  • Returns:

    • The amount that can be withdrawn immediately without queuing.

onERC721Received

Handles the receipt of an ERC721 NFT.

solidityCopy codefunction onERC721Received(
    address _operator,
    address _from,
    uint256 _tokenId,
    bytes memory _data
) external override returns (bytes4)
  • Returns:

    • The selector indicating successful receipt.

receive

Fallback function to accept Ether deposits.

solidityCopy codereceive() external payable
  • Logic:

    • Allows the contract to receive Ether without data.


Usage Examples

Depositing Ether

solidityCopy codevault.depositToken{value: amount}(address(0), amount);
  • Deposits amount of Ether into the vault.

Depositing ERC20 Tokens

solidityCopy codevault.depositToken(tokenAddress, amount);
  • Before calling, ensure the vault has approval to transfer the tokens.

Depositing an NFT

solidityCopy codevault.depositNFT(nftContractAddress, tokenId);
  • Transfers the specified NFT into the vault.

Withdrawing Tokens Within Limits

solidityCopy codevault.withdrawToken(recipientAddress, tokenAddress, amount);
  • If amount is within the immediate withdrawal limit, the transfer occurs immediately.

Queuing a Withdrawal Exceeding Limits

solidityCopy codevault.withdrawToken(recipientAddress, tokenAddress, largeAmount);
  • If largeAmount exceeds the immediate withdrawal limit, the withdrawal is queued.

Confirming a Queued Transaction

solidityCopy codevault.confirmTransaction(txIndex);
  • Called by the owner or whitelisted addresses to confirm a queued transaction.

Canceling a Queued Transaction

solidityCopy codevault.cancelTransaction(txIndex);
  • Cancels a transaction in the queue.

Setting Token Limits via the Contract Itself

First, queue a transaction to call setTokenLimit on the vault:

solidityCopy codebytes memory data = abi.encodeWithSignature(
    "setTokenLimit(address,uint256,uint256,uint256)",
    tokenAddress,
    fixedLimit,
    percentageLimit,
    useBaseLimit
);
vault.queueTransaction(address(vault), data, 0);

Then, confirm and execute the transaction as per the confirmation process.


Important Notes

  • Ownership and Access Control: The owner has special privileges but cannot bypass certain security mechanisms like the freeze status or confirmation thresholds.

  • Withdrawal Limits: The vault enforces withdrawal limits to enhance security. Exceeding the limit requires transaction queuing and confirmations.

  • Freeze Mechanism: The freeze status can be set to prevent unauthorized transactions. Only the recovery address can decrease the freeze level.

  • Recovery Address: The recovery address has elevated privileges, including the ability to recover assets and modify critical settings.

  • Security Considerations: The contract is designed with security in mind, incorporating multi-signature approvals, delayed executions, and restricted access to sensitive functions.

  • Upgradeable Contract: The vault uses a proxy pattern for upgradeability, allowing the implementation to be updated via the SimpleProxy contract.


SimpleProxy Contract Documentation

The SimpleProxy contract is a minimal proxy that delegates all calls to an implementation contract using the delegatecall mechanism. This pattern allows for upgradeable contracts by separating the logic (implementation) from the storage (proxy).


Key Components

Variables

  • address public immutable implementation: The address of the implementation contract to which calls are delegated.

Constructor

solidityCopy codeconstructor(address _implementation)
  • Parameters:

    • _implementation: The address of the implementation contract.

  • Requirements:

    • _implementation must not be the zero address.

Functions

_delegate

Internal function to delegate calls to the implementation contract.

solidityCopy codefunction _delegate(address impl) internal virtual
  • Parameters:

    • impl: The address of the implementation contract.

  • Logic:

    • Uses inline assembly to perform a delegatecall, forwarding all call data and returning any received data.

fallback

Fallback function that delegates all calls to the implementation contract.

solidityCopy codefallback() external payable virtual

receive

Receive function to handle plain Ether transfers.

solidityCopy codereceive() external payable virtual

VaultFactory2 Contract Documentation

The VaultFactory2 contract is responsible for deploying new instances of the Vault2 contract using the SimpleProxy. It allows users to create their own vaults with customizable settings and keeps track of deployed vaults.


Key Components

Variables

  • address public owner: The owner of the factory contract.

  • uint256 public totalVaults: The total number of vaults created.

  • address public vaultImplementation: The address of the current vault implementation contract.

  • mapping(string => address) public vaultNames: Maps vault names to their corresponding vault addresses.

  • mapping(address => address[]) public ownerToVaults: Maps owner addresses to their vaults.

Events

  • VaultCreated

    Emitted when a new vault is created.

    solidityCopy codeevent VaultCreated(
        address vaultAddress,
        address indexed owner,
        string name,
        address recoveryAddress
    );

Modifiers

  • onlyOwner

    Restricts the function to be called only by the owner.

    solidityCopy codemodifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }

Functions

Constructor

solidityCopy codeconstructor(address _vaultImplementation)
  • Parameters:

    • _vaultImplementation: The address of the vault implementation contract.

  • Logic:

    • Sets the factory owner (in this code, a hardcoded address is used).

    • Sets the vault implementation address.

createVault

Allows users to create a new vault with specified settings.

solidityCopy codefunction createVault(
    string memory _name,
    address _recoveryAddress,
    address[] memory _whitelistedAddresses,
    uint256 _dailyLimit,
    uint256 _threshold,
    uint256 _delay
) public returns (address)
  • Parameters:

    • _name: The name of the new vault.

    • _recoveryAddress: The recovery address for the vault.

    • _whitelistedAddresses: Addresses to be whitelisted in the vault.

    • _dailyLimit: The base daily withdrawal limit (in percentage).

    • _threshold: The number of confirmations required for transaction execution.

    • _delay: The delay before a transaction can be executed.

  • Logic:

    • Ensures the vault name is unique.

    • Generates a unique salt based on the sender and their vault count.

    • Deploys a new SimpleProxy pointing to the current vault implementation.

    • Initializes the vault via the proxy.

    • Updates mappings and counters.

    • Emits VaultCreated.

updateVaultImplementation

Allows the factory owner to update the vault implementation contract.

solidityCopy codefunction updateVaultImplementation(address _vaultImplementation) public onlyOwner
  • Parameters:

    • _vaultImplementation: The new vault implementation address.

getVaultsByOwner

Retrieves all vaults associated with a specific owner.

solidityCopy codefunction getVaultsByOwner(address _owner) public view returns (address[] memory)
  • Parameters:

    • _owner: The address of the vault owner.

  • Returns:

    • An array of vault addresses owned by the specified owner.


Usage Flow

  1. Deploy the Factory: The VaultFactory2 contract is deployed with the address of the Vault2 implementation contract.

  2. Create a Vault:

    • A user calls createVault on the factory with their desired settings.

    • The factory deploys a new SimpleProxy contract.

    • The new vault is initialized via the proxy, pointing to the Vault2 implementation.

  3. Interact with the Vault:

    • Users can deposit tokens and NFTs into their vault.

    • The owner can withdraw assets, subject to the vault's withdrawal limits and security mechanisms.

    • Transactions that exceed limits are queued and require confirmations.

  4. Manage Transactions:

    • Queued transactions can be confirmed by the owner and whitelisted addresses.

    • Transactions are executed once they meet the required confirmations and delay.

  5. Update Settings:

    • The owner or recovery address can update vault settings by queuing and executing transactions that call the vault's update functions.

  6. Recovery and Security:

    • In case of compromised access, the recovery address can recover assets and set the freeze status to protect the vault.


Important Notes

  • Factory Owner: The factory owner can update the vault implementation, affecting future vaults. Existing vaults are not affected unless they choose to upgrade.

  • Proxy Pattern: The use of the SimpleProxy allows for upgradeable contracts. Users should be aware of the implementation address their vault proxy is pointing to.

  • Security Considerations:

    • Users should ensure the vault implementation is secure and audited.

    • Proper key management is essential to maintain control over vault assets.

    • The recovery address should be secured, as it has significant control over the vault.

  • Vault Names: Vault names must be unique across the factory to prevent naming collisions.


Conclusion

The Vault2, SimpleProxy, and VaultFactory2 contracts provide a comprehensive solution for secure asset management on the Ethereum blockchain. They offer customizable security features, including withdrawal limits, multi-signature transaction confirmations, and recovery mechanisms.

Users and developers should thoroughly understand the contract functionalities and ensure secure practices when deploying and interacting with these contracts. Regular audits and adherence to security best practices are recommended to safeguard assets managed by the vault.

Last updated