$RENT on Pons. Contract address: launch soon Follow @rentdayxyz for the address
Rentday
CAsoon

Operator guide

The steps the operator follows once at launch and then on the 1st of every month.

Before you begin

This page is for the operator. Holders can read it to see what happens on rent day.

The contracts commands on this page match the scripts in contracts/package.json as written today. If a script name or argument changes, contracts/README.md is the reference. Every script that sends a transaction does a dry run by default and broadcasts only with --send.

The scripts read their settings from the environment, and from contracts/.env when that file exists.

VariableUsed byMeaning
PRIVATE_KEYdeploy, and add-rent and publish-roll with --sendThe key that signs. Keep it in contracts/.env and never commit it.
RPC_URLAll scriptsDefaults to https://rpc.mainnet.chain.robinhood.com.
VAULT, REGISTRYadd-rent, publish-roll, verify-rollDefault to the addresses in contracts/deployments/<chainId>.json, which the deploy writes.

One-time launch

  1. Deploy both contracts. With PRIVATE_KEY set, run the deploy from contracts/ with the mainnet confirmation shown below. On chain 4663 the script refuses to run without CONFIRM_MAINNET=yes. It deploys RentdayVault and RollRegistry, makes a seed deposit so the opening price is 1.000000 USDG per vRENT, checks that price, and writes contracts/deployments/4663.json. Optional settings: OWNER (owner of both contracts, default the deployer), CAP_USDG (deposit cap, default 100000), SEED_USDG (seed deposit, default 100) and USDG (default the USDG address).
  2. Configure the site. In site/js/config.js, set ADDR.vault and ADDR.registry to the deployed addresses, ADDR.rentToken to the $RENT contract, and LINKS.pons to the $RENT page on Pons. config.js is the only site file that changes at launch.
  3. Write site/data/portfolio.json with the real buildings, in the same shape as SAMPLE_PORTFOLIO in site/js/sample-data.js (example below).
  4. Deploy the site. Once ADDR.vault is set, the site switches from pre-launch to live reads.
cd contracts
CONFIRM_MAINNET=yes npm run deploy

The portfolio file lists each building with its units. This example uses the first sample building, cut to two units:

{
  "buildings": [
    {
      "id": "A",
      "city": "Cleveland",
      "state": "OH",
      "kind": "Brick walk-up",
      "built": 1926,
      "floors": 3,
      "cols": 2,
      "appraised": "690000",
      "note": "Three floors over a corner shop that is not part of the vault.",
      "units": [
        { "unit": "101", "beds": 1, "rent": "1040" },
        { "unit": "102", "beds": 1, "rent": "1060" }
      ]
    }
  ]
}

The building id and the unit number form the unit key used everywhere else, such as A-101. appraised and rent are USDG amounts written as decimal strings. Keep every field the sample has. An optional fees object overrides the default rates, using the keys of DEFAULT_FEES in site/js/close.js.

Once site/data/rolls/index.json lists a published Roll, the site drops the sample Rolls, and every Sample label disappears on its own.

The monthly close

The close runs at 00:00 UTC on the 1st, for the month that has ended. In order:

  1. Collect the month's facts.
  2. Write the month file.
  3. Run the close.
  4. Settle the USDG.
  5. Publish the file.
  6. Commit the Roll on chain.
  7. Verify.

Steps 4 to 6 must happen in that order. The price committed in step 6 is read from the vault, so the USDG has to be settled first, and the publish script checks the public file, so the site has to be deployed first.

Collect the month's facts

For the month that has ended, gather:

  • each apartment's status at the close: paid, late or vacant;
  • late rent that arrived during the month for earlier periods;
  • spending on repairs and upkeep, and on capital works;
  • any change to a building's appraised value, with the reason;
  • the $RENT token fee: the ETH claimed, the USDG the swap produced, and the claim and swap transaction hashes.

Write the month file

Put the facts in a JSON file. The values below are an example.

{
  "n": 1,
  "period": "2026-10",
  "published": "2026-11-01",
  "status": { "A-202": "late", "C-201": "vacant" },
  "recovered": [],
  "repairs": "2310.00",
  "capital": "0",
  "tokenFees": { "eth": "1.4200", "usdg": "4388.20", "claimTx": "0x...", "swapTx": "0x..." },
  "appraisals": [],
  "note": "One or two sentences on anything unusual this month."
}
FieldMeaning
nRoll number: the registry's latest() plus 1. The first Roll is 1.
periodThe month closed, as YYYY-MM.
publishedThe publication date, as YYYY-MM-DD: the 1st of the next month.
statusMap from unit key to "late" or "vacant". Any apartment not listed is paid.
recoveredList of { "unit": "A-202", "forPeriod": "2026-10" } entries for late rent that arrived this month. An optional amount records a partial payment; without it, the apartment's lease rent is used.
repairsUSDG spent on repairs and upkeep, as a decimal string.
capitalUSDG spent on capital works.
tokenFees{ "eth", "usdg", "claimTx", "swapTx" }. Leave it out in a month with no token fee.
appraisalsList of { "building": "B", "change": "45000", "reason": "..." } entries. change is in USDG, negative for a fall.
noteA line of context shown on the site. It is not part of the hashed file.

Run the close

node tools/close.mjs month.json

From the project root, npm run close -- month.json does the same. The tool reads the portfolio (site/data/portfolio.json), the previous published Roll, and the vault's current totalAssets() and totalSupply() from chain. It writes site/data/rolls/NNN.csv (001.csv for Roll 1) and prints the amount to settle. Read the new file before going further, and run the checks on Reading a Roll.

Settle the USDG

Settle straight after the close. A deposit or cash-out between the two changes the vRENT count the Roll used, and the price after settlement would then differ from the Roll's price_close.

cd contracts
npm run add-rent -- <amount>
npm run add-rent -- <amount> --send

The first command is a dry run. It prints the vault's USDG and share price before and after. The second approves the exact amount if the current allowance is short, then calls addCapital(amount). Amounts take at most six decimals, and the script rejects anything finer.

Settle the token-fee USDG the same way, with a second add-rent. In a shortfall month, when settled is 0, skip the rent transfer and settle the token fee alone. After both transfers, the share price the script prints should match the Roll's price_close to six decimals; a difference of 0.000001 can come from rounding.

Publish the file

Add an entry for the Roll to site/data/rolls/index.json:

{"n":1,"file":"001.csv","period":"2026-10","label":"October 2026","published":"2026-11-01","note":"..."}

Then deploy the site, so the file is public at its final address, here https://rentday.xyz/data/rolls/001.csv, before anything goes on chain. The host must serve the file unchanged, with no line-ending conversion.

Commit the Roll on chain

A published Roll can never be changed or deleted. Read the dry-run output in full before you add --send.

cd contracts
npm run publish-roll -- 1 ../site/data/rolls/001.csv https://rentday.xyz/data/rolls/001.csv
npm run publish-roll -- 1 ../site/data/rolls/001.csv https://rentday.xyz/data/rolls/001.csv --send

The dry run prints the SHA-256 of the local file and the navPerShare it will write, then runs its checks: the Roll number must be latest() + 1, the sender must own the registry, and if the URI is reachable, the file there must hash the same as the local one. It then simulates the call. With --send it publishes and reads the Roll back to confirm every field.

navPerShare is read from the vault at that moment, as convertToAssets(1e18) × 1,000,000, which is why settlement comes first. If the URI serves different bytes from the local file, the script refuses to send.

Verify

npm run verify-roll -- 1 https://rentday.xyz/data/rolls/001.csv

The script reads rolls(1) from chain, downloads the file and prints MATCH or MISMATCH. Then open /roll#verify in a browser and confirm the page shows the same result.

When something goes wrong

  • A published Roll has an error. Publish the correction in the next Roll. The original stays on chain.
  • publish-roll refuses. The Roll number is wrong, the sender is not the registry owner, or the file at the URI differs from the local file. Nothing was sent; fix the cause and run it again.
  • add-rent refuses. The amount has more than six decimals, the sender holds too little USDG, or the vault has no vRENT yet, in which case addCapital would revert.