Skip to content

Latest commit

 

History

History
162 lines (129 loc) · 7.72 KB

File metadata and controls

162 lines (129 loc) · 7.72 KB

Contributing

Thanks for helping build this. The repo is a pnpm/Turborepo monorepo with two areas: apps/web + packages/ui (the front end) and apps/contracts (Solidity). Most changes touch only one; the setup and checks below are split accordingly.

Local setup

pnpm install
cp apps/web/.env.example apps/web/.env.local
pnpm dev

pnpm@11 and Node 22 (or 20.19+) — vite-node requires a narrower range than the engines field. Vite does not read .env.example, so the copy is not optional: without .env.local the app fail-fasts at startup and names the missing variable.

Everything in .env.example is a public Sepolia address. The only value you may want to add is VITE_PRIVY_APP_ID; without it every read-only screen still works and the Connect button explains why it is disabled.

Contract tooling (Hardhat, Foundry, Slither) lives under apps/contracts — see the "Contracts" section of the root README for its own setup.

Before opening a PR

Front end (apps/web, packages/ui):

pnpm --filter web... typecheck
pnpm --filter web... lint
pnpm --filter web... build
pnpm --filter web exec vite-node scripts/check-abi.ts     # no network
pnpm --filter web exec vite-node scripts/check-units.ts   # no network

If the change touches a contract call, also run the two that hit Sepolia:

pnpm --filter web exec vite-node scripts/verify-onchain.ts
pnpm --filter web exec vite-node scripts/verify-reverts.ts

Contracts (apps/contracts):

pnpm --filter rwa-contracts-evm build
pnpm --filter rwa-contracts-evm typecheck
pnpm --filter rwa-contracts-evm lint
FOUNDRY_FUZZ_SEED=1 pnpm --filter rwa-contracts-evm test

CI runs the same sets, split into ci-web.yml and ci-contracts.yml, each only triggered by changes under its own area. The on-chain front-end pair is a separate, non-blocking job — a public RPC rate-limiting should not turn a PR red — so read its output rather than assuming green.

What the front-end checks are for

typecheck, lint and build catch the ordinary things. The extra scripts exist because this domain fails silently:

  • check-units pins the preview arithmetic, rounding direction included. A reimplementation of the vault's fee maths gets the direction wrong without erroring.
  • check-abi asserts the vendored error inventory, including that the merged ABI still carries an error the called contract's own ABI lacks. Without that property, nested reverts decode to nothing and every failure reads as "execution reverted".
  • verify-onchain compares live Sepolia against a reference snapshot and asserts the metrics batch arrives as a single multicall chunk — the single-block guarantee behind TVL = totalSupply × NAV.
  • verify-reverts reproduces seven reverts by eth_call and asserts each maps to a human sentence. No wallet or gas required.

When one of these disagrees with the chain, that is a fact about the deployment, not a defect in the check. Record the diff and escalate — do not adjust the reference to match.

Working with contract data in the front end

Read apps/web/src/lib/units.ts before touching anything that carries an amount. The traps that have already cost time:

  • wBOND is 18 decimals, the payment token is 6. The deposit vault charges its fee in token units before converting; the redemption vault charges in wBOND.
  • minAmountWad means different things on the two vaults — payment-token value on one, wBOND on the other. Same getter name, different store.
  • Request.minOut is WAD for a deposit and payment-token units for a redemption, despite the argument being named outWad.
  • DataFeed.getPrice() reverts when the price is unhealthy. Batches run with allowFailure: true for that reason; removing it blanks the whole screen on a stale oracle.

Regenerate the ABIs from apps/contracts rather than editing apps/web/src/lib/abi/ by hand:

pnpm --filter rwa-contracts-evm build
pnpm --filter web exec vite-node scripts/gen-abi.ts

Conventions worth knowing

  • Addresses live in apps/web/src/lib/config.ts only. A CI check greps for any other 0x literal under apps/web/src.
  • Every useReadContracts passes batchSize: 0, in the call parameters and never on the client — on the client it enables a different viem mechanism entirely.
  • UI primitives come from packages/ui. Add them with pnpm --filter @workspace/ui exec shadcn add <name>; do not re-implement one locally.
  • Blocking vs warning. A reason the user cannot fix by typing (no wallet, wrong network, paused) disables the form. A reason that depends on the amount (below minimum, over balance) only warns — the input has to stay editable, because typing 24 passes through 2.
  • Comments explain why, not what. If a line needs a comment to say what it does, rename something instead.

Before you start on something non-trivial

Open an issue and get a nod first — don't spend real time on a new feature or a non-obvious fix before it's been discussed. This isn't process for its own sake: it's the difference between a PR that merges same-day and one that sits because the approach needed to change. Small fixes and obvious bugs don't need this — use judgment.

Git, commits, and PRs

  • Commits are informative. Not feat: almost completed or fix: stuff. A commit message should say what changed and, where it's not obvious, why.
  • Never commit broken code. Each commit is a real, working snapshot. If you need to push work-in-progress to remote as a backup, push it to its own branch — don't land it on the branch others will build on.
  • Split commits reasonably. Prefer several focused commits (git add -p is useful here) over one large commit that bundles unrelated changes.
  • No dead code, no old/, no archive/. Git already keeps full history — commented-out code and "just in case" folders don't need to live in the tree. Delete it; it's recoverable from history if it's ever actually needed.
  • Merge only when the branch is actually done. Don't merge early just to hand off a partial fix to someone else — use git cherry-pick (or just ask) instead of merging an unfinished branch into a shared one. Opening a PR early for visibility or feedback is fine — mark it a draft so it's clear it isn't ready to merge yet; that's a different thing from merging unfinished work.
  • Refactor for a reason, not for taste. A drive-by rewrite of working code because it "reads better" your way creates a large diff with no behavior change and makes history harder to trace. Refactor when it's needed for the fix or feature at hand, or file it as its own separate PR with a concrete justification (perf, a bug it enables fixing, etc.) — don't bundle it into an unrelated change.
  • Before solving something from scratch, check if it's already been solved. If the task is something the industry has already built many times over — wallet connection, KYC integration, a common DeFi primitive — look at how it's usually done first. Re-deriving a well-trodden pattern from zero is rarely the best use of the time.

Code review

Before requesting review, check your own change against the task/ticket it's solving and give it a self-review pass — most review cycles that go back and forth repeatedly are cases where this step got skipped. From there: automated checks (lint/typecheck/build/ test) run first, then a human review. Don't merge your own PR unless the process explicitly allows it.

Security-sensitive changes

If your change touches wallet connection, signing, token approvals, slippage, contract privileges, or anything that could move funds, see SECURITY.md before opening the PR.