npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cityofzion/monopoly-sdk

v0.5.2

Published

A package for interfacing with the monopoly backend

Readme

Overview

@cityofzion/monopoly-sdk exposes typed calls for the deployed Monopoly contract:

  • games contain locations, and locations contain properties
  • a property purchase creates or resumes an address's game session
  • sessions can have negative balances
  • player actions authenticate through an ITEM-backed asset proof
  • permissions are a dictionary on every user; there are no separate user/admin models

Registration, OTP, login, and profile functions are not smart-contract methods. They remain available through monopoly.api, a dedicated REST client.

Installation

npm install @cityofzion/monopoly-sdk

Contract setup

Create a signer-backed instance for writes. scriptHash is required for real contract calls. The SDK's itemScriptHash is the ITEM registry used only to resolve an asset record; the Monopoly contract dynamically resolves the record's bound token contract.

import { Monopoly, constants } from "@cityofzion/monopoly-sdk";

const monopoly = await Monopoly.init({
  node: constants.NeoN3NetworkOptions.TestNet,
  scriptHash: "0x...",
  account,
});

Configure a game

Contract callers must hold the applicable permission dictionary entries. The SDK does not maintain a separate admin identity: the signer determines authorization.

const gameId = await monopoly.createGameSync();

await monopoly.setStartingBalanceSync({
  gameId,
  startingBalance: 1500,
});

await monopoly.setTipsSync({
  gameId,
  tips: [101, 202, 303],
});

const locationId = await monopoly.createLocationSync({
  gameId,
  name: "Copacabana",
});

const propertyId = await monopoly.createPropertySync({
  locationId,
  name: "Avenida Atlantica",
  cost: 200,
  probability: 60,
  results: [-100, 400],
});

results contains [failureResult, successResult]. A purchase returns the selected gross result; the session balance changes by result - cost, so a player may enter debt.

Read game state

getGameJSON returns location summaries and the configured tips inventory. Use getLocationJSON to retrieve that location's full property records. getPropertyJSON reads one property, while getPropertiesJSON batches multiple property reads into one test invocation.

const game = await monopoly.getGameJSON({ gameId });
const location = await monopoly.getLocationJSON({ locationId });
const property = await monopoly.getPropertyJSON({ propertyId: 1 });
const properties = await monopoly.getPropertiesJSON([1, 2, 3]);

console.log(game.locations);
console.log(location.properties);

getUserJSON returns the user permissions and expanded sessions grouped by game id. getUserGameSessionsJSON retrieves the sessions for one game directly. Each session includes ownedPropertyIds and ownedProperties. The latter records each selected property's propertyId, whether the successful outcome was selected in result, and its gross outcome in value. Sessions also include availableTips, their independent remaining inventory copied from the game's tips when the session began, and ownedTips, the values collected.

const user = await monopoly.getUserJSON({
  address: "N...",
});

const sessions = await monopoly.getUserGameSessionsJSON({
  address: "N...",
  gameId,
});

Purchase and end a session

The contract accepts one payload containing the asset public key and the three proof values. It resolves the asset's binding contract internally, so callers must not provide an AeroGlyph or binding script hash.

const auth = {
  assetPubKey: "02...",
  message: "...",
  proof: "...",
  challenge: "01",
};

const result = await monopoly.purchasePropertySync({
  gameId,
  propertyId,
  auth,
});

const tip = await monopoly.getTipSync({ gameId, auth });

await monopoly.endGameSessionSync({ gameId, auth });

getTip creates or resumes the authenticated holder's active session, then consumes one random remaining tip. It rejects with Monopoly: Out of tips when the session inventory is exhausted.

The PropertyPurchase notification contains address, gameId, sessionId, propertyId, cost, gross result, and post-settlement balance. Decode a notification with decodeMonopolyEvent.

Backend API

The off-chain service remains accessible, but is deliberately separate from contract behavior:

await monopoly.api.requestOTP("[email protected]");
const login = await monopoly.api.login("[email protected]", "123456");

Mock transport

Mock mode implements the same current contract hierarchy and purchase/session behavior without a node or wallet.

const monopoly = await Monopoly.init({
  mock: { latencyMs: 0, scenario: "debt" },
});

const result = await monopoly.purchasePropertySync({
  gameId: 1,
  propertyId: 1,
  auth,
});

Development

npm ci
npm run tsc
npm test
npm run lint
npm run format

Localhost Integration Test

The opt-in localhost suite exercises standard permissions, game configuration, location/property hierarchy, and read workflows against the current Neo Express Monopoly deployment. It starts the configured node when it is not already running and leaves the local chain state intact. It does not deploy external contracts or run asset-authenticated player methods.

npm run test:localhost

By default, the runner expects the contract repository at ../monopoly-contract-n3. Override the local deployment inputs when needed:

MONOPOLY_NEOXP_CONFIG=/path/to/default.neo-express \
MONOPOLY_SCRIPT_HASH=0x... \
npm run test:localhost

Asset-authenticated player calls and contract upgrade are intentionally excluded. The former requires a production-synced asset fixture; the latter requires replacement NEF and manifest artifacts and is not part of ordinary gameplay.

Deployment and Update

The SDK provides generic RPC-and-wallet deployment commands. They do not use Neo Express: deploy uses the SDK's Utils.deployContract helper, while update invokes Monopoly.updateSync against the supplied contract hash. Compile the contract before either command and provide the generated NEF and manifest paths explicitly.

MONOPOLY_RPC=https://your-rpc.example:443 \
MONOPOLY_PRIVATE_KEY=your-private-key-or-wif \
MONOPOLY_NEF=/absolute/path/Monopoly.nef \
MONOPOLY_MANIFEST=/absolute/path/Monopoly.manifest.json \
npm run deploy

For an update, add the deployed Monopoly contract hash. MONOPOLY_UPDATE_DATA is an optional string passed to the updated contract's _deploy method and defaults to null; this contract does not require it. MONOPOLY_TIMEOUT_MS defaults to 60000 milliseconds.

MONOPOLY_RPC=https://your-rpc.example:443 \
MONOPOLY_PRIVATE_KEY=your-private-key-or-wif \
MONOPOLY_SCRIPT_HASH=0x... \
MONOPOLY_NEF=/absolute/path/Monopoly.nef \
MONOPOLY_MANIFEST=/absolute/path/Monopoly.manifest.json \
npm run update

Create a Game

npm run create-game -- path/to/game.json creates a configured game through the SDK, then prints its game id. Start with examples/game.config.template.json. The script deactivates the newly created game while it adds locations and properties, and activates it only after every configuration transaction succeeds. On an error, the game remains inactive for manual review.

Each location and property accepts an optional active boolean that defaults to true. startingBalance, every tip, costs, probabilities, and both property results must be safe integers; property results must contain exactly two values.

MONOPOLY_RPC=https://your-rpc.example:443 \
MONOPOLY_PRIVATE_KEY=your-private-key-or-wif \
MONOPOLY_SCRIPT_HASH=0x... \
MONOPOLY_TIMEOUT_MS=120000 \
npm run create-game -- /absolute/path/game.json

Update Game Properties

npm run update-game-properties -- <game-id> path/to/game.json updates every property in an existing game from the same configuration format. It matches locations and properties by name, validates the entire hierarchy before making any writes, then submits one property update per configured property. Optional property active flags are also applied. It does not modify the game's starting balance, tips, active state, or any location configuration.

MONOPOLY_RPC=https://your-rpc.example:443 \
MONOPOLY_PRIVATE_KEY=your-private-key-or-wif \
MONOPOLY_SCRIPT_HASH=0x... \
MONOPOLY_TIMEOUT_MS=120000 \
npm run update-game-properties -- 1 /absolute/path/game.json

Public API

  • Monopoly: typed contract facade
  • MonopolyRestAPI: dedicated registration and profile REST client
  • createMonopolyMock: ABI-aligned local transport
  • decodeMonopolyEvent: contract notification decoder
  • types, constants, and helpers: supporting public modules

License

License details are not yet specified in this repository.