Welcome to Day 8 of my #30DaysOfSolidity challenge!
Todayβs build: a Multi-Currency Digital Tip Jar β where users can send Ether directly or simulate tips in other currencies like USD or EUR.
Think of it like a decentralized βBuy Me a Coffeeβ button β global, borderless, and blockchain-powered. βπ
πͺ Project Overview
The TipJar contract allows:
- π° Real ETH tips using
payable
. - π Simulated USD/EUR tips (based on conversion rates).
- π§Ύ Tracking of every userβs total contributions.
- π Owner-only withdrawals.
Itβs a simple concept with real-world applications β from global donations to decentralized tipping platforms.
π§ Key Concepts Learned
- Handling Ether transfers using
msg.value
andpayable
. - Using mappings to track contributions per user.
- Implementing enums to support multiple currencies.
- Managing conversion rates with owner control.
- Emitting events for transparency and analytics.
βοΈ Smart Contract β TipJar.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title TipJar - Multi-currency digital tip jar (ETH + simulated USD/EUR)
/// @author Saurav
/// @notice Accepts ETH tips and simulated foreign currency tips
/// @dev Owner manages conversion rates; all contributions stored in wei-equivalent
contract TipJar {
address public owner;
enum Currency { USD, EUR }
// Conversion: wei-per-cent (1 cent of USD/EUR = X wei)
mapping(Currency => uint256) public weiPerCent;
// Track real ETH and simulated (converted) contributions
mapping(address => uint256) public ethContributed;
mapping(address => uint256) public simulatedWeiContributed;
uint256 public totalEthReceived;
uint256 public totalSimulatedWei;
// Events
event EtherTipped(address indexed from, uint256 amountWei, string message);
event CurrencyTipped(address indexed from, Currency currency, uint256 amountCents, uint256 weiEquivalent, string message);
event RateUpdated(Currency currency, uint256 weiPerCent);
event Withdraw(address indexed to, uint256 amountWei);
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
constructor(uint256 usdWeiPerCent, uint256 eurWeiPerCent) {
owner = msg.sender;
weiPerCent[Currency.USD] = usdWeiPerCent;
weiPerCent[Currency.EUR] = eurWeiPerCent;
}
/// @notice Tip with Ether
function tipEther(string calldata message) external payable {
require(msg.value > 0, "Send some ETH");
ethContributed[msg.sender] += msg.value;
totalEthReceived += msg.value;
emit EtherTipped(msg.sender, msg.value, message);
}
/// @notice Simulate USD/EUR tip
function tipCurrency(Currency currency, uint256 amountCents, string calldata message) external {
require(amountCents > 0, "Amount must be > 0");
uint256 rate = weiPerCent[currency];
require(rate > 0, "Rate not set");
uint256 weiEquivalent = amountCents * rate;
simulatedWeiContributed[msg.sender] += weiEquivalent;
totalSimulatedWei += weiEquivalent;
emit CurrencyTipped(msg.sender, currency, amountCents, weiEquivalent, message);
}
/// @notice Update conversion rate (owner only)
function setRate(Currency currency, uint256 newWeiPerCent) external onlyOwner {
require(newWeiPerCent > 0, "Rate must be > 0");
weiPerCent[currency] = newWeiPerCent;
emit RateUpdated(currency, newWeiPerCent);
}
/// @notice Withdraw ETH (owner only)
function withdraw(address payable to, uint256 amountWei) external onlyOwner {
require(amountWei <= address(this).balance, "Not enough balance");
(bool ok, ) = to.call{value: amountWei}("");
require(ok, "Transfer failed");
emit Withdraw(to, amountWei);
}
/// @notice Get total (ETH + simulated) wei contributed by user
function totalWeiBy(address user) external view returns (uint256) {
return ethContributed[user] + simulatedWeiContributed[user];
}
receive() external payable {
ethContributed[msg.sender] += msg.value;
totalEthReceived += msg.value;
emit EtherTipped(msg.sender, msg.value, "");
}
}
π‘ How It Works
- Users can tip using Ether directly (real payment).
- Users can simulate tips in USD or EUR, converted to wei based on the ownerβs set rate.
- The contract stores both real and simulated contributions for each address.
- The owner manages exchange rates and can withdraw ETH from the contract.
π’ Example Conversion
If 1 ETH = $2,000 β
1 cent = 1e18 / 200,000 = 500000000000000 wei
So, to set the USD rate:
setRate(Currency.USD, 500000000000000)
If 1 ETH = β¬1,800 β
1 cent = 1e18 / 180,000 = 5555555555555 wei
Use that for EUR rate.
π§© Real-World Applications
- A global tip jar for developers and creators.
- A donation system supporting multiple currencies.
- A foundation for multi-currency DeFi and dApp payment systems.
π Future Upgrades
- π Integrate Chainlink oracles for live currency rates.
- π΅ Support more fiat and crypto currencies.
- π§ Add a frontend dashboard for live supporter stats.
- π§° Implement multi-sig ownership for secure fund control.
π§Ύ GitHub Source Code
π View the complete code on GitHub
π¬ Reflection
This project helped me understand how financial logic, conversion systems, and smart contracts can come together to power global applications.
The blockchain doesnβt care about borders β and neither should appreciation. π
β Summary
A simple idea β tipping β turned into a global decentralized system.
With Solidity, we can reimagine how appreciation and contributions flow β without borders, intermediaries, or limits.
Top comments (0)