Skip to content

smartargs/neow3j-react-starter

Repository files navigation

neow3j-react-starter

A working starter for Neo N3 dApps with a Java/Kotlin smart contract compiled via neow3j and a React/Vite UI that talks to the chain via neon-js + Reown AppKit. Forks cleanly into a real project.

What's in the box

contract/   Java contract (CounterContract — placeholder), compiled to NEF
            via the neow3j gradle plugin. Has a JUnit 5 test suite that
            runs against a Dockerized single-node privatenet via
            neow3j-test (Docker must be running).
ui/         React + Vite SPA. Connects via NeoLine (extension) and/or
            WalletConnect (Reown AppKit). Reads + writes the contract.
            Vitest unit tests for the chain glue + a Playwright e2e suite
            (no wallet — see "Testing").
localnet/   Convenience scripts to spin up an AxLabs neo3-privatenet via
            docker for local end-to-end work.
.github/    CI: gradle test; ui unit tests, typecheck, build, e2e,
            bundle-size budget gate.

Quickstart

# 1. Compile the contract (writes NEF + manifest into contract/build/neow3j/)
./gradlew :contract:neow3jCompile

# 2. Run the contract tests (JDK 17 or 21; not 25 — see STACK_NOTES.md)
./gradlew :contract:test

# 3. Start the UI (auto-runs sync scripts that bundle the freshly-compiled NEF)
cd ui
npm install
npm run dev

The UI defaults to mainnet RPC. For local end-to-end:

./localnet/start.sh                # docker compose up -d
VITE_NETWORK=localnet npm run dev  # uses the Vite /__rpc proxy

Forking this template

Search-and-replace these tokens, in order:

  1. CounterContract → your contract class name (Java + tests + sync scripts).
  2. com.example.counter → your Java package. Also set the @ManifestExtra Author value in CounterContract.java (ships as your-name-here).
  3. neow3j-react-starter → your repo / app name (root settings.gradle.kts, ui/package.json, ui/src/lib/appkit.ts metadata).
  4. neow3jstarter.* storage-key prefixes in ui/src/lib/connection.tsx and ui/src/lib/rpc.ts.

Then write your contract, replace the methods in ui/src/lib/contract.ts (and its tests in ui/src/lib/contract.test.ts, plus the stubbed methods in ui/e2e/mock-rpc.ts), and rewrite App.tsx around them. Everything else (vite config, gradle, CI, wallet adapters, the test harnesses) stays.

Configuration

Copy ui/.env.example to ui/.env.local and fill in what you need. All vars are optional — with none set, the UI runs against mainnet RPC in NeoLine-only mode.

VITE_NETWORK=mainnet            # mainnet | testnet | localnet
VITE_RPC_URL=                   # optional override (any neo-cli RPC)
VITE_WC_PROJECT_ID=             # Reown project id; empty = NeoLine-only
VITE_CONTRACT_HASH=0x…          # default contract for the UI to read

Contract:

  • gradle.properties: neow3jVersion, JVM flags.
  • contract/build.gradle.kts: className must point at your contract.

Testing

Contract — JUnit 5 against a Dockerized single-node privatenet (Docker must be running):

./gradlew :contract:test              # full suite
./gradlew :contract:test --tests CounterContractTest.increment_byOwner_bumpsCountAndFiresEvent

The test extension boots a Neo node in a Docker container, deploys all @ContractTest contracts as the genesis multi-sig, and exposes them via ext.getDeployedContract. See helpers/TestHelper.java for the genesis-funded GAS helper and the assertAborted pattern (read FAULT messages from the application log).

UI — two layers:

cd ui
npm run test          # vitest: unit tests of src/lib (RPC mocked) — fast, no chain
npm run e2e:install   # one-time: download the chromium binary
npm run e2e           # playwright: builds, serves dist/, drives a real browser

vitest covers the chain glue in src/lib — stack-item decoding, contractExists, waitForTx, and the shape of the invocation increment() hands the wallet — with the RPC layer mocked.

playwright (e2e/) drives the built bundle in a headless browser. Its job is less "UI coverage" and more "did the polyfill / manualChunks config in vite.config.ts survive" — that config only fails at runtime, so a build that passes can still ship a blank page. It also exercises the read path against a tiny stub RPC (e2e/mock-rpc.ts).

There's no wallet-driven e2e on purpose. NeoLine is a browser extension and WalletConnect needs a real wallet on the other end; automating either buys little over the increment() unit test and is a flake magnet. If you need it, inject a fake window.NEOLineN3 via page.addInitScript whose invoke signs with a funded localnet key — but for most forks the unit + no-wallet-e2e split is enough.

License

MIT.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors