Field notes
Sixteen NEAR contract exercises
Introduction
I worked through all 16 exercises on NEARbyExample: nine fundamentals and seven advanced modules.
The goal was not to rush the checklist. I wanted a clear picture of how each API behaves, what reads versus writes cost, and which patterns reappear in shipped dApps.
Basics
Greeting contract
One view method returns a string: the smallest useful contract boundary.
Takeaway
Public functions on the contract are what wallets and web clients call; the return path is the same whether the logic is trivial or not.
Where it shows up
- User onboarding messages
- Profile greetings
Contract structure
State fields such as owner_id and greeting live on the contract struct.
Takeaway
Deployment and caller identity matter: predecessorAccountId() tells you who invoked the method, which is how you gate writes.
Where it shows up
- Token contracts (owner controls minting)
- Admin dashboards
View methods
Read-only methods: no persistent state change.
Takeaway
Views do not commit storage writes, so callers can poll balances and metadata without paying gas for the read path itself.
Where it shows up
- Checking balances
- Reading user data
- Displaying contract state on the frontend
Change methods
Mutating calls persist changes and consume gas.
Takeaway
Every write has a cost; product flows should batch work and avoid noisy updates.
Where it shows up
- Sending tokens
- Updating records
- Writing data to the blockchain
State management
Persistent counter incremented across transactions.
Takeaway
On-chain state outlives a single request; design around explicit transitions and migrations, not implicit resets.
Where it shows up
- NFT ownership tracking
- Game progress storage
- User balances
Input validation
Reject empty or out-of-range input before it touches storage.
Takeaway
Arguments cross a trust boundary; validate early and fail loud.
Where it shows up
- Prevent invalid transactions
- Avoid contract misuse
Error handling
Soft failures with null; invariant breaks with panic.
Takeaway
Use recoverable returns when the caller can adjust; panic when continuing would corrupt state or funds.
Where it shows up
- Prevent loss of funds
- Ensure contract safety
Collections: Vector
Ordered lists via the SDK Vector, not a plain JS array.
Takeaway
On-chain collections have storage prefixes and different cost profiles; swap_remove is not a casual splice.
Where it shows up
- Lists of NFTs
- Product listings
- User-generated content
Collections: Map
Key-value storage for accounts and scores.
Takeaway
Maps are the default shape for balances, settings, and leaderboards keyed by account.
Where it shows up
- Account balances
- Leaderboards
- User-specific settings
Advanced
Collections: Map (leaderboard)
Rank entries and return the top N from the map.
Takeaway
Full sorts in a view are fine at exercise scale; at production scale you plan for iterator cost and frequency.
Where it shows up
- Gaming leaderboards
- Reward systems
- Competitions
Events
Structured logs for state-changing methods.
Takeaway
UIs and indexers listen to logs; the contract does not push to a browser by itself.
Where it shows up
- Transaction tracking
- Notifications
- Analytics dashboards
Owner pattern
Owner-only mutators, public views.
Takeaway
A single privileged account is the baseline pattern for admin parameters and treasury-style operations.
Where it shows up
- Admin-only settings
- Token minting permissions
Role-based access
Separate owners and admins with distinct capabilities.
Takeaway
Layered roles map cleanly to teams and DAOs; checks live next to each entry point.
Where it shows up
- DAO governance
- Team-managed projects
Pausable contract
Owner toggles pause; mutators respect the flag.
Takeaway
A narrow circuit breaker on writes limits damage while keeping reads available for monitoring.
Where it shows up
- Prevent damage during incidents
- Pause during upgrades
Multi-signature
Proposals need a quorum before execution.
Takeaway
Thresholds reduce single-key risk for treasuries and high-impact calls.
Where it shows up
- DAO treasury management
- Shared wallets
Upgrade pattern
Version fields, migrate steps, and guarded writes during upgrades.
Takeaway
Deployed bytecode is fixed; migrations bridge one stored shape to the next. Rust leans on attributes such as #[init(ignore_state)]; the JS track leans on explicit reset and wiring.
Where it shows up
- Fixing bugs after deployment
- Adding new features
What stuck
- Permissions are the product. If admin paths are wide open, nothing else you build matters.
- How you store state (maps, vectors, versioning) drives gas, upgrade risk, and what the frontend can query cheaply.
- Upgrades and migrations are doable but brittle; Rust and JavaScript diverge exactly when you touch lifecycle hooks.
Friction
- Upgrade and migrate flows needed more guided narration in the UI.
- Copy still leans Rust-shaped even in the JS track, which slows context switching.
- I would have liked a tighter link to storage cost and production-sized collection patterns.
Closing
The track is a coherent arc. You leave with workable habits around views and calls, collections, events, pause and multisig, and a respect for how much careful design sits behind a small public API.
I typed each solution myself, executed the tests, and captured screenshots as I finished each lesson. If you are on NEAR, working the set end to end is worthwhile.