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

Greeting exercise: JavaScript contract editor and completed task panel

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

Contract Structure exercise: owner_id, greeting state, and owner-only set_greeting pattern

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

View Methods exercise: LookupMap user greetings and read-only view functions

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

Change Methods exercise: owner-protected call methods, message validation, and view helpers

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

State Management exercise: persistent counter with increment and get_counter

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

Input Validation exercise: message board with non-empty and max-length checks on set_message

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

Error Handling exercise: null-safe parsing, safe_divide, parse_with_default, and near.panic guards

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

Collections: Vector exercise: list manager with SDK Vector, StorageKey, tags, and swap_remove

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

Collections: Map exercise: IterableMap leaderboard, set_score, get_score, get_top_scores

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)

Collections: Map: leaderboard sorting with IterableMap, get_top_scores

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

Events exercise: NEP-297 style logging, MessageUpdated and MessageDeleted from set_message and delete_message

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 Pattern exercise: owner-only setValue and transferOwnership, getValue view, require checks

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

Role-Based Access exercise: owners and admins, multi-role require checks, admin_only_action

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

Pausable Contract exercise: owner pause or unpause, guarded increment, get_counter always readable

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

Multi-Signature exercise: owners, proposals, approve, execute with threshold, payable deposit

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

Upgrade Pattern exercise: data_version, owner pause or unpause and migrate, guarded set_value, get_version

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.