Honour the Lord with thy substance, and give him of the first of all thy fruits.
Charitable giving on Ethereum. Patrons stake Ether, which the vault converts to Rocket Pool rETH. Principal stays the patron's and is withdrawable at any time; staking yield is directed to causes of the patron's choosing, in percentages they choose.
rETH does not rebase — balances are constant and the rETH:Ether exchange rate
rises as staking rewards accrue. Firstfruits records each patron's rETH and
their Ether-denominated principal. The permissionless harvest(patron) skims
exactly the rETH no longer needed to back that principal at the current rate
and distributes it per the patron's designation — up to 32 (causeId, bps)
pairs summing to at most 100%.
Whatever the designation doesn't cover goes to the patron's bank: yield
held in rETH (so it keeps appreciating) until the patron directs it or takes
it back. (Re)designating applies the new split to the bank exactly once — a
split summing to 100% drains it — and withdrawBank/withdrawBankReth
reclaim it.
Causes are permissionless: anyone may create one with createCause. Each
cause has an owner (manages recipient/active status, two-step transferable)
and a recipient. Claiming as rETH is permissionless (funds only ever move to
the recipient); claiming as Ether is up to the cause owner or recipient. The
vault owner's only power is sweeping surplus from direct rETH donations to a
cause.
contracts/src/Firstfruits.sol— the vaultcontracts/src/interfaces/— minimal Rocket Pool interfaces (addresses are resolved throughRocketStorage, which survives protocol upgrades)
The contracts/ directory holds the Foundry
project; dependencies (Solady,
forge-std) are managed with Soldeer. All commands run
from contracts/:
cd contracts
forge soldeer install # fetch dependencies (first checkout only)
forge build
forge testSolidity sources are formatted with dove preen. Mainnet fork tests
(make test-fork) validate the Rocket Pool and CreateX integration and need
MAINNET_RPC_URL set in .env.
Deployment goes through the
CreateX factory so Firstfruits can
be pinned to a salt-ground address (CREATE3 addresses depend only on the
factory and the salt, not the creation code). Project-pinned defaults
(ROCKET_STORAGE, CREATEX) live in .env.maintainer; deployer
configuration (RPC, deployer private key, ground salt and its expected
address, vault owner) is documented in .env.example. From the repository root:
cp .env.example .env # fill in the values
make deploy-dry # simulate; verifies the CreateX address matches
make deploy # broadcastmake help lists all goals.