Tally, end to end.

Forward the receipts you already get, receive fractional tokenized stock you own. Here is every moving part — and the complete on-chain source.

Overview

Tally reads the subscription receipts you already receive by email and pays you a small reward in fractional tokenized stock. Pay for Netflix, hold a sliver of NFLX. Pay for ChatGPT or Claude, hold a sliver of NVDA.

Nothing is minted. Every reward is a transfer of stock already sitting in a public vault, so the on-chain balance is honest, verifiable backing. You connect nothing but a forwarding rule — Tally never logs into your inbox and only reads the receipts you forward.

How it works

  1. Get your address. A unique earn+<id>@TallyMoneyRH.app that maps to your account.
  2. Forward only receipts. A mail filter scoped to subscription senders forwards matching mail there — keeping 2FA codes and password resets out.
  3. Tally parses it. Merchant, USD amount, date; the merchant maps to a tokenized stock.
  4. Tally verifies it. Valid DKIM that is domain-aligned to the real merchant, deduped by Message-ID, addressed to you, within the freshness window. Fail any check, earn nothing.
  5. Your reward is signed. Your $TALLY holdings set your tier and rate; the remaining monthly cap applies; an EIP-712 voucher is signed for the exact stock at the live price. Rate locked at earn time.
  6. You claim. Your own wallet submits the voucher to the vault and the stock transfers to you. Tally never touches your key.

What earns a reward

A subscription pays in the brand’s own tokenized stock when it is live on Robinhood Chain — Netflix → NFLX, Apple → AAPL, Amazon → AMZN, Meta → META, Starlink → SPCX, Steam → TTWO. Everything else falls back to its sector: Everyday → GOOGL, AI → NVDA, big cloud (AWS, Azure, GitHub, Google Cloud) → MSFT, and independent hosting (Hetzner, Vultr, Cloudflare, Vercel…) → QQQ. Every stock is live on RH Chain, so every reward is claimable. Generated from the live merchant map:

GOOGL · Alphabet
Adobe, Disney+, DoorDash, Google, Google Play, HBO Max, Hulu, Max, Spotify, Uber, X, YouTube, YouTube Premium
QQQ · Nasdaq-100 ETF
Cloudflare, Cloudzy, Contabo, DigitalOcean, Fly.io, Hetzner, Linode, Netlify, OVHcloud, Railway, Render, Vercel, Vultr
NVDA · NVIDIA
ChatGPT, Claude, Cursor, ElevenLabs, Hugging Face, Midjourney, NVIDIA, Perplexity, Replicate
MSFT · Microsoft
AWS, Azure, GitHub, Google Cloud, Microsoft, Xbox
AMZN · Amazon
Amazon, Prime Video
SPCX · SpaceX
SpaceX, Starlink
AAPL · Apple
Apple
META · Meta
Meta
NFLX · Netflix
Netflix
TTWO · Take-Two
Steam

More to come

This list keeps growing — new merchants and stocks go live regularly. Only a completed, DKIM-verified receipt from a covered merchant pays.

Reward tiers

Your rate is set by how much $TALLY you hold, valued in USD at its best price over the last 24 hours. No lock-up required. Each tier caps rewards per month.

Tier$TALLY heldRateMonthly cap
Base$05%$25
Bronze$2508%$60
Silver$1,00011%$150
Gold$2,50015%$400
Black$5,00020%$900
Founder$10,00026%$2,000
  • Rate at earn time. The rate on a receipt is the one in effect when it is recorded, then locked into the voucher.
  • Best-24h valuation. A brief price dip does not knock you down a tier.
  • Monthly cap. Rewards above the cap in a month are not paid; it resets monthly.

Custody

TallyRewardVault holds the stock that backs rewards. It has no owner sweep and no arbitrary execute(). Stock leaves by exactly one path: a user claim() against a Tally-signed voucher. There is no admin-withdraw, sweep, or drain function; stock only flows in, by a plain transfer from the treasury. Releases are authorized by the reward signer (below), so the vault is only as trustworthy as that key is kept — which is why every signer rotation is recorded on-chain.

Because every payout is a transfer of stock already in the vault, its on-chain balances are honest backing you can check any time on the Proof page.

Payouts are authorized by Tally's reward signer key, which is secured accordingly; every rotation is recorded on-chain (SignerSet). This is the standard model for signature-authorized reward claims.

Privacy & anti-abuse

Why a forged receipt cannot pay you

  • DKIM + alignment. The signature must be valid and its domain aligned to the real merchant. Editing a forwarded email breaks it.
  • Uniqueness. Rewards dedupe on the immutable Message-ID (a body-hash covers receipts without one).
  • Ownership. The receipt's original recipient must include your own email.
  • Freshness. Receipts older than the window are rejected.
  • Monthly caps. Each tier bounds how much one account earns per month.

What Tally can and cannot see

  • Only the receipts you forward: merchant, amount, date. Never your inbox, never your password.
  • Revoke any time by deleting the filter — no account access was granted to claw back.
  • Claims are signed by your own browser wallet; the key never leaves it.

Contracts

The reward vault, on Robinhood Chain (id 4663), MIT-licensed and non-custodial. The complete, unmodified source is below — nothing elided.

TallyRewardVault

claim(token, to, amount, id, deadline, sig)
The only way stock leaves the vault. Verifies the EIP-712 voucher against the signer, enforces single-use id and deadline, transfers stock to to.
reserves(token) → uint256
Live vault balance of one reward stock — the proof-of-reserves read.
setRewardToken(token, allowed)
Owner: add or remove a stock from the payout allow-list.
setSigner(s)
Owner: rotate the reward-signer key. Emits SignerSet. The trust anchor.
setPaused(p)
Owner: emergency stop for claims. Cannot move funds.

That is the entire owner surface. No sweep, no execute(), no fee-pull, no receive() — nothing moves a token out except claim().

contract/src/TallyRewardVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

/// @title TallyRewardVault
/// @notice Pre-funded, MULTI-STOCK reward vault for Tally. Holds a basket of
/// tokenized stocks (NVDA, GOOGL, MSFT, NFLX, AMZN, QQQ, ...) and releases the
/// exact reward only against a Tally-signed EIP-712 voucher. Nothing is minted;
/// every payout is a transfer of stock already sitting in the vault, so the
/// on-chain balance is honest proof of reserves.
///
/// The reward `amount` already encodes the user's $TALLY hold-to-tier rate: the
/// Tally signer reads the user's on-chain $TALLY holdings, applies the tier rate
/// (5% base -> up to 26% Founder) and monthly cap, and signs the final amount. The
/// contract only verifies the signature, the reward-token allow-list, single-use
/// id, and deadline — the economics live off-chain in the signer.
///
/// NON-CUSTODIAL: there is deliberately NO owner sweep and NO arbitrary execute().
/// Stock leaves the vault ONLY via a user claim() against a signed voucher, so the
/// operator cannot withdraw the backing. Stock flows IN by a plain ERC-20 transfer
/// from the treasury (the operator claims pons ETH creator fees to its own EOA, buys
/// tokenized stock off-chain, and deposits it here). Payouts are authorized by the
/// reward `signer` key (see below).
contract TallyRewardVault is EIP712, Ownable {
    address public signer;   // Tally claim server key that co-signs approvals
    bool public paused;

    mapping(address => bool) public isRewardToken;  // tokenized-stock allow-list
    mapping(bytes32 => bool) public voucherUsed;     // reward id => redeemed
    mapping(address => uint256) public totalPaid;    // per-token lifetime payout

    bytes32 public constant VOUCHER_TYPEHASH = keccak256(
        "Reward(address token,address to,uint256 amount,bytes32 id,uint256 deadline)"
    );

    event Claimed(address indexed token, address indexed to, uint256 amount, bytes32 indexed id);
    event RewardTokenSet(address indexed token, bool allowed);
    event SignerSet(address indexed signer);
    event PausedSet(bool paused);

    constructor(address signer_, address[] memory rewardTokens_)
        EIP712("Tally Rewards", "1")
        Ownable(msg.sender)
    {
        require(signer_ != address(0), "signer=0");
        signer = signer_;
        for (uint256 i = 0; i < rewardTokens_.length; i++) {
            require(rewardTokens_[i] != address(0), "token=0");
            isRewardToken[rewardTokens_[i]] = true;
            emit RewardTokenSet(rewardTokens_[i], true);
        }
    }

    /// @notice Live proof of reserves for one reward stock.
    function reserves(address token) external view returns (uint256) {
        return IERC20(token).balanceOf(address(this));
    }

    // ---------------------------------------------------------------- claim ---

    /// @notice Redeem an approved reward. Anyone may relay the voucher, but the
    /// stock always goes to `to`. Single-use per `id`, expires at `deadline`.
    function claim(
        address token,
        address to,
        uint256 amount,
        bytes32 id,
        uint256 deadline,
        bytes calldata sig
    ) external {
        require(!paused, "paused");
        require(isRewardToken[token], "token");
        require(to != address(0), "to=0");
        require(block.timestamp <= deadline, "expired");
        require(!voucherUsed[id], "used");

        bytes32 structHash = keccak256(abi.encode(VOUCHER_TYPEHASH, token, to, amount, id, deadline));
        require(ECDSA.recover(_hashTypedDataV4(structHash), sig) == signer, "bad sig");

        voucherUsed[id] = true;
        totalPaid[token] += amount;
        require(IERC20(token).transfer(to, amount), "transfer failed");
        emit Claimed(token, to, amount, id);
    }

    // ---------------------------------------------------------------- admin ---
    // NON-CUSTODIAL: the vault has NO owner path to move reward funds OUT. Stock
    // leaves ONLY via a user `claim` against a Tally-signed voucher. There is no
    // sweep, no arbitrary `execute`, and no fee-pull that could trap ETH here, so
    // the operator cannot withdraw the backing. Stock is funded IN by a plain
    // ERC-20 transfer from the treasury EOA.
    //
    // The reward `signer` is a Tally operator key that authorizes each earned-reward
    // voucher. It is secured accordingly, and any rotation is recorded on-chain via
    // SignerSet. This is the standard model for signature-authorized reward claims.

    function setRewardToken(address token, bool allowed) external onlyOwner {
        require(token != address(0), "token=0");
        isRewardToken[token] = allowed;
        emit RewardTokenSet(token, allowed);
    }

    function setSigner(address s) external onlyOwner {
        require(s != address(0), "signer=0");
        signer = s;
        emit SignerSet(s);
    }

    /// @notice Pausing only stops payouts; it cannot move funds (emergency stop).
    function setPaused(bool p) external onlyOwner {
        paused = p;
        emit PausedSet(p);
    }
}

On-chain addresses

Everything is on Robinhood Chain (id 4663) — verify any of these on the explorer. The vault, treasury, and signer refresh live; the $TALLY token address is published here the moment it launches on pons.

$TALLY tokenLaunches on pons, paired with ETH0xEC77AbFF3722Db66A568C32C2A21F9D171C845a6
TallyRewardVaultNon-custodial reward vault0x533f214AC40b21512C33bDb2988CA94c0E8c709A
Treasury walletReceives fees · buys stock · funds the vault0x798f4462d9ED34aEbe34Cf7D1b9a664B2F1485C8
Reward signerCo-signs reward vouchers (public key)0x2231Ec24FDFf7C15A37f3b8891550BA7f42b005f
NVDAAI — ChatGPT, Claude, Perplexity, Cursor0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC
GOOGLGoogle, YouTube · Everyday-sector fallback0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3
MSFTMicrosoft, GitHub, Xbox · Cloud sector (AWS, Azure, hosting)0xe93237C50D904957Cf27E7B1133b510C669c2e74
QQQIndependent hosting & infra (Hetzner, Vultr, Cloudflare, Vercel…)0xD5f3879160bc7c32ebb4dC785F8a4F505888de68

CLI

The CLI talks only to the Tally API and prints results. It never reads local files, runs shell commands, or asks for a private key.

install & run
npx github:TallyMoneyRH/tally-cli#v0.2.0 <command>

tally connect <email> [--wallet 0x..]   provision address + forwarding steps
tally status  <addr_id>                 rewards, tier, claimable vouchers
tally claim   <addr_id>                 show vouchers + how to claim safely
  • connect creates your address, optionally binds a wallet, then watches for Gmail's confirmation code.
  • status shows this month's rewards, your tier and rate, and how much more to hold for the next tier.
  • claim prints the exact claim() call to submit from a wallet you control.

MCP server

Lets an AI agent connect an inbox and manage rewards conversationally. Four tools, marked read vs. write:

tally_connect(email, wallet?)
WRITE. Creates a new forwarding address and returns the setup steps. Only call when the user wants to connect.
tally_status(addr_id)
READ. Rewards, tier + rate, holdings, next tier, claimable count.
tally_verification(addr_id)
READ. The Gmail forwarding confirmation code once it arrives.
tally_claim_calldata(addr_id)
READ. claim() calldata for each voucher — public calldata only, no signing.
run the server
npx github:TallyMoneyRH/tally-mcp#v0.2.0
# then register it with your agent as an MCP stdio server

FAQ

Can Tally take my rewards back?

No. Once stock is in the vault the only way it moves is a user claim(). There is no owner withdrawal — read the source above.

Do I have to hold $TALLY to earn?

No. The Base tier earns 5% with zero holdings; holding more raises your rate and cap.

Is this financial advice or a securities offering?

No. Tally is a rewards utility. Tokenized stocks are held and transferred on Robinhood Chain; rewards are not guaranteed returns, and eligibility depends on Robinhood Chain and your jurisdiction.

How do I stop using Tally?

Delete the forwarding filter. Tally has no other access. Anything you already claimed stays in your wallet.