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
- Get your address. A unique
earn+<id>@TallyMoneyRH.appthat maps to your account. - Forward only receipts. A mail filter scoped to subscription senders forwards matching mail there — keeping 2FA codes and password resets out.
- Tally parses it. Merchant, USD amount, date; the merchant maps to a tokenized stock.
- 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.
- 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.
- 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:
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 held | Rate | Monthly cap |
|---|---|---|---|
| Base | $0 | 5% | $25 |
| Bronze | $250 | 8% | $60 |
| Silver | $1,000 | 11% | $150 |
| Gold | $2,500 | 15% | $400 |
| Black | $5,000 | 20% | $900 |
| Founder | $10,000 | 26% | $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
id and deadline, transfers stock to to.SignerSet. The trust anchor.That is the entire owner surface. No sweep, no execute(), no fee-pull, no receive() — nothing moves a token out except claim().
// 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.
0xEC77AbFF3722Db66A568C32C2A21F9D171C845a60x533f214AC40b21512C33bDb2988CA94c0E8c709A0x798f4462d9ED34aEbe34Cf7D1b9a664B2F1485C80x2231Ec24FDFf7C15A37f3b8891550BA7f42b005f0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE30xe93237C50D904957Cf27E7B1133b510C669c2e740x12f190a9F9d7D37a250758b26824B97CE941bF540x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa0xE0444EF8BF4eD74f74FD73686e2ddF4C1c5591E80xD5f3879160bc7c32ebb4dC785F8a4F505888de68CLI
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.
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 safelyconnectcreates your address, optionally binds a wallet, then watches for Gmail's confirmation code.statusshows this month's rewards, your tier and rate, and how much more to hold for the next tier.claimprints the exactclaim()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:
npx github:TallyMoneyRH/tally-mcp#v0.2.0
# then register it with your agent as an MCP stdio serverFAQ
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.