@cityofzion/monopoly-sdk
v0.5.2
Published
A package for interfacing with the monopoly backend
Keywords
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-sdkContract 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 formatLocalhost 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:localhostBy 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:localhostAsset-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 deployFor 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 updateCreate 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.jsonUpdate 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.jsonPublic API
Monopoly: typed contract facadeMonopolyRestAPI: dedicated registration and profile REST clientcreateMonopolyMock: ABI-aligned local transportdecodeMonopolyEvent: contract notification decodertypes,constants, andhelpers: supporting public modules
License
License details are not yet specified in this repository.
