Automated Savings Vault
What it does:
Users deposit funds into a smart contract vault that automatically enforces savings rules. Withdrawals are locked until predefined conditions are met (time, goals, milestones), encouraging disciplined and consistent saving.
Why it matters:
Many people struggle to save because of impulsive spending. By removing emotional decision-making and enforcing rules on-chain, this contract helps users build strong financial habits and long-term wealth.
How it works:
-
Users deposit ETH into a personal savings vault.
-
A lock period or target goal is defined at creation.
-
Funds cannot be withdrawn until conditions are satisfied.
-
Optional penalties apply for early withdrawal attempts.
-
Progress and savings history are transparently stored on-chain.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title AutomatedSavingsVault
* @author Nam
* @notice A smart contract that enforces disciplined saving by locking funds
* until a predefined condition is met.
*/
contract AutomatedSavingsVault {
struct Vault {
uint256 balance;
uint256 unlockTime;
uint256 targetAmount;
bool targetBased;
bool withdrawn;
}
mapping(address => Vault) public vaults;
event VaultCreated(
address indexed user,
uint256 unlockTime,
uint256 targetAmount,
bool targetBased
);
event Deposit(
address indexed user,
uint256 amount,
uint256 totalBalance
);
event Withdraw(
address indexed user,
uint256 amount
);
event EarlyWithdrawPenalty(
address indexed user,
uint256 penaltyAmount
);
/**
* @notice Create a new savings vault
* @param _unlockTime Timestamp when funds can be withdrawn
* @param _targetAmount Target savings goal (0 if time-based only)
*/
function createVault(
uint256 _unlockTime,
uint256 _targetAmount
) external payable {
require(vaults[msg.sender].balance == 0, "Vault already exists");
require(msg.value > 0, "Initial deposit required");
bool targetBased = _targetAmount > 0;
vaults[msg.sender] = Vault({
balance: msg.value,
unlockTime: _unlockTime,
targetAmount: _targetAmount,
targetBased: targetBased,
withdrawn: false
});
emit VaultCreated(
msg.sender,
_unlockTime,
_targetAmount,
targetBased
);
}
/**
* @notice Deposit additional funds into an existing vault
*/
function deposit() external payable {
Vault storage vault = vaults[msg.sender];
require(vault.balance > 0, "Vault does not exist");
require(!vault.withdrawn, "Vault already withdrawn");
require(msg.value > 0, "Deposit must be greater than zero");
vault.balance += msg.value;
emit Deposit(msg.sender, msg.value, vault.balance);
}
/**
* @notice Withdraw funds if conditions are met
*/
function withdraw() external {
Vault storage vault = vaults[msg.sender];
require(vault.balance > 0, "Vault does not exist");
require(!vault.withdrawn, "Already withdrawn");
bool timeConditionMet = block.timestamp >= vault.unlockTime;
bool targetConditionMet = !vault.targetBased || vault.balance >= vault.targetAmount;
require(timeConditionMet || targetConditionMet, "Withdrawal conditions not met");
uint256 amount = vault.balance;
vault.withdrawn = true;
vault.balance = 0;
payable(msg.sender).transfer(amount);
emit Withdraw(msg.sender, amount);
}
/**
* @notice Emergency early withdrawal with penalty (10%)
*/
function emergencyWithdraw() external {
Vault storage vault = vaults[msg.sender];
require(vault.balance > 0, "Vault does not exist");
require(!vault.withdrawn, "Already withdrawn");
uint256 penalty = (vault.balance * 10) / 100;
uint256 payout = vault.balance - penalty;
vault.withdrawn = true;
vault.balance = 0;
payable(msg.sender).transfer(payout);
emit EarlyWithdrawPenalty(msg.sender, penalty);
emit Withdraw(msg.sender, payout);
}
/**
* @notice View vault details
*/
function getVault(address _user)
external
view
returns (
uint256 balance,
uint256 unlockTime,
uint256 targetAmount,
bool targetBased,
bool withdrawn
)
{
Vault memory vault = vaults[_user];
return (
vault.balance,
vault.unlockTime,
vault.targetAmount,
vault.targetBased,
vault.withdrawn
);
}
}