Writing
Coffee Chain — Decentralized Tipping Platform
Coffee Chain — Support Creators with ETH and Messages

Coffee Chain — A Decentralized Tipping Platform on Ethereum

I built Coffee Chain, a decentralized Ethereum platform where supporters tip creators with ETH and attach messages — like a Web3 "Buy Me a Coffee". No platform holds the funds, no intermediary takes a cut. Every tip is a transparent, permanent on-chain transaction.

The Problem With Centralized Tipping

Platforms like Buy Me a Coffee and Patreon sit between creators and their supporters. They hold the money, set the rules, and take a fee. A creator's earnings are only ever as reliable as the platform running them.

On-chain tipping removes the middleman entirely. A supporter sends ETH straight to a smart contract, the tip lands in the creator's balance, and the message is recorded on-chain forever. There's no one to trust and nothing to censor.

The Contract

Coffee.sol is a single, self-contained contract. The core concept is a Memo — a tip with a message:

struct Memo {
    address from;
    uint256 timestamp;
    string name;
    string message;
}

Creator Registration

Anyone can register as a creator with a name and a short bio. The contract enforces a few rules:

  • Names must be non-empty
  • An address can only register once
  • Names are unique — a creatorByName mapping (keyed by keccak hash) prevents impersonation
function registerCreator(string calldata _name, string calldata _about) external {
    if (bytes(_name).length == 0) revert EmptyName();
    if (creators[msg.sender].owner != address(0)) revert AlreadyRegistered();
    ...
}

Profiles can be updated later — including a rename — as long as the new name isn't taken.

Buying Coffee

Sending a tip is one transaction: the supporter names the creator, writes a message, and attaches ETH. The contract records the memo, bumps the creator's running total, and emits a NewCoffee event:

function buyCoffee(address payable _creator, string calldata _name, string calldata _message)
    external payable
{
    if (msg.value == 0) revert NoFundsSent();
    if (creators[_creator].owner == address(0)) revert CreatorNotRegistered();

    memosByCreator[_creator].push(
        Memo({from: msg.sender, timestamp: block.timestamp, name: _name, message: _message})
    );
    creators[_creator].totalReceived += msg.value;
    creatorBalances[_creator] += msg.value;

    emit NewCoffee(_creator, msg.sender, msg.value, block.timestamp, _name, _message);
}

Withdrawals

Creators withdraw their own balance directly to their wallet. The withdrawal follows the Checks-Effects-Interactions pattern — the balance is zeroed before the ETH transfer, and restored if the transfer fails:

function withdraw() external {
    if (creators[msg.sender].owner == address(0)) revert NotACreator();
    uint256 amount = creatorBalances[msg.sender];
    if (amount == 0) revert NoFundsToWithdraw();

    creatorBalances[msg.sender] = 0;  // effects before interaction
    (bool success,) = msg.sender.call{value: amount}("");
    if (!success) {
        creatorBalances[msg.sender] = amount;  // roll back on failure
        revert WithdrawFailed();
    }
    emit FundsWithdrawn(msg.sender, amount);
}

Gas-Friendly Reads

Reading memos is designed for on-chain data. getMemosPaginated returns a bounded window with an offset, so a creator with thousands of tips can page through them without hitting the gas limit — a real concern with unbounded array returns.

Testing

The Foundry test suite covers the full surface: registration and uniqueness, name collisions, tipping flows, and withdrawal behavior:

forge test

Frontend

The frontend is a Next.js app with wallet connection via Reown AppKit (formerly WalletConnect). The routes map cleanly to the contract's features:

  • / — the marketing home page
  • /[username] — a public creator profile with a support form and a live list of past memos
  • /create — register as a creator
  • /dashboard — the creator dashboard with a withdraw button and support history
  • /docs — contract address and deployment info

Supporters attach a name and message with their tip; creators see their supporters and messages aggregated from on-chain state, not a database.

Deployment

The contract is deployed on Sepolia at 0x035efEe092383e2baFdAAAacF79167c55178fa59 and verified on Etherscan. The live app runs at coffee-chain-sepolia.vercel.app — Rakib's profile is at coffee-chain-sepolia.vercel.app/rakib.

Reflections

The design that matters most here is no custody. Funds never sit in a platform treasury — they're either with the supporter or the creator, and every transaction is public. That's the whole pitch of on-chain tipping, and the contract keeps it honest: no fee middleware, no admin that can freeze funds, just a creator, a supporter, and a message recorded on the blockchain.