A smart contract network built on top of Krist
Noot is a deterministic state machine that processes transactions from the Krist blockchain to provide smart contract functionality. It enables token creation, minting, burning, and transfers using Krist's transaction metadata system.
Note: This project was developed silently 2 years ago and never released. Given that SwitchCraft 3 has shut down and Krist is less widely used today, this project is being released purely as educational material to demonstrate a smart contract implementation on top of Krist.
Krist is a virtual currency and economy system that works across servers, originally created for the Minecraft mod ComputerCraft. Krist provides a simple transaction-based blockchain where users can send currency between addresses and attach metadata to transactions.
Noot extends Krist by interpreting specially-formatted transaction metadata as smart contract operations. It maintains a deterministic state by processing Krist transactions in order, validating operations, and building an immutable chain of state changes.
Think of Noot as a Layer 2 solution for Krist - it uses Krist transactions as its data layer while providing higher-level functionality like custom tokens.
- Token System: Create, mint, burn, and transfer custom tokens
- Deterministic State: All nodes processing the same transactions reach identical state
- Immutable History: Complete audit trail of all operations via journaling
- Real-time Sync: WebSocket connection to Krist for instant transaction processing
- Type-Safe: Built with TypeScript and Zod for runtime validation
- SQLite Storage: Efficient local database with full transaction history
graph TB
Krist[Krist Blockchain<br/>Transaction Data Source]
subgraph NootClient[Noot Client]
VM[Transaction Processor VM<br/>- Parses CommonMeta format<br/>- Validates operations<br/>- Applies state changes]
Contracts[Contract System<br/>- Genesis chain initialization<br/>- CreateToken, MintToken, BurnToken<br/>- TransferToken, TransferTokenOwnership<br/>- Journaling state validation]
DB[SQLite Database<br/>- Chain state snapshots<br/>- Transaction history<br/>- Noot message log]
end
Krist -->|WebSocket + HTTP API| VM
VM --> Contracts
Contracts --> DB
Noot maintains a ChainState
object that contains:
- Tokens: All created tokens with their metadata and balances
- Owner: The address that initialized the chain (via Genesis)
- Genesis: Initial chain configuration
- Journal: Last processed message metadata for validation
Each transaction is processed through the VM which:
- Parses the transaction metadata using CommonMeta format
- Validates the operation against current state
- Applies state changes using Immer (immutable updates)
- Records the new state and journals the operation
- Stores everything in SQLite for persistence
Noot messages are embedded in Krist transaction metadata using the CommonMeta format:
noot;v=0;n=T;op=CT;tkn=0;nam=MyToken;sym=MTK;dec=1000
All messages include:
v
: Protocol versionn
: Network (N=mainnet, T=testnet)op
: Operation code
Initializes a new Noot chain. Must be the first operation.
Parameters:
msg
: Genesis message string
Example:
{
v: 0,
n: NootNetwork.TESTNET,
op: NootOperation.GENESIS,
msg: "NOOT NOOT"
}
Creates a new token with specified properties.
Parameters:
tkn
: Token identifier (numeric)nam
: Token namesym
: Token symboldec
: Decimals (for display purposes)
Example:
{
v: 0,
n: NootNetwork.TESTNET,
op: NootOperation.CREATE_TOKEN,
tkn: 0,
nam: "Noot Token",
sym: "NOOT",
dec: 1000
}
Creates new tokens and assigns them to an address. Only the token owner can mint.
Parameters:
tkn
: Token identifierdst
: Destination addressamt
: Amount to mint
Example:
{
v: 0,
n: NootNetwork.TESTNET,
op: NootOperation.MINT_TOKEN,
tkn: 0,
dst: "kxxxxxxxxx",
amt: 1000
}
Transfers tokens between addresses.
Parameters:
tkn
: Token identifierdst
: Destination addressamt
: Amount to transfer
Example:
{
v: 0,
n: NootNetwork.TESTNET,
op: NootOperation.TRANSFER_TOKEN,
tkn: 0,
dst: "kyyyyyyyy",
amt: 100
}
Permanently destroys tokens from the sender's balance.
Parameters:
tkn
: Token identifieramt
: Amount to burn
Example:
{
v: 0,
n: NootNetwork.TESTNET,
op: NootOperation.BURN_TOKEN,
tkn: 0,
amt: 50
}
Transfers ownership of a token to another address. Only the current owner can do this.
Parameters:
tkn
: Token identifierdst
: New owner address
Example:
{
v: 0,
n: NootNetwork.TESTNET,
op: NootOperation.TRANSFER_TOKEN_OWNERSHIP,
tkn: 0,
dst: "kzzzzzzzz"
}
A no-op operation that can be used for signaling or testing.
Parameters: None
# Install dependencies
pnpm install
# Build the project
pnpm build
# Run tests
pnpm test
# Development mode
pnpm dev
# Production mode
pnpm build
node dist/src/cli.js
Create a config.json
file in the project root:
{
"dbPath": "chain.db",
"network": "T",
"kristApiUrl": "https://krist.dev",
"privateKey": "your-krist-private-key",
"unsafe": false
}
Configuration Options:
dbPath
: Path to SQLite database file (default:chain.db
)network
: Network to operate on -N
(mainnet) orT
(testnet) (default:T
)kristApiUrl
: Krist API endpoint (optional, uses default if not specified)privateKey
: Your Krist private key for posting transactions (optional, read-only without it)unsafe
: If true, crashes on invalid transactions instead of skipping them (default:false
)startFromTransaction
: Transaction ID to start syncing from (optional, see Fast-Forward Mode below)
By default, Noot uses fast-forward mode to skip old blockchain history and start from transaction 2739857
. This dramatically improves startup time for new instances.
Automatic Fast-Forward:
{
"dbPath": "chain.db",
"network": "T"
}
- Automatically starts from transaction 2739857
- Fast startup (seconds instead of minutes)
- Only recent chain data available
Custom Starting Point:
{
"dbPath": "chain.db",
"network": "T",
"startFromTransaction": 2800000
}
Full Historical Sync:
{
"dbPath": "chain.db",
"network": "T",
"startFromTransaction": 0
}
- Syncs from the beginning of the chain
- Slower startup but complete history
import { NootClient } from "@tmpim/noot";
// Create and start a client
const client = new NootClient();
await client.start();
// Post a message (requires privateKey in config)
const noot = await client.postMessage({
v: 0,
n: NootNetwork.TESTNET,
op: NootOperation.CREATE_TOKEN,
tkn: 0,
nam: "My Token",
sym: "MTK",
dec: 1000,
});
console.log("Created noot:", noot);
Noot uses SQLite with Drizzle ORM for data persistence:
krist_transactions
: All Krist transactions
- Indexed by: from, to, time
- Stores raw transaction data
noots
: Processed Noot messages
- Indexed by: transaction_id, op, from, to, token, amount
- Stores parsed message data and state patches
chain_state
: Chain state snapshots
- Stores complete state after each Noot message
- Includes hash for integrity verification
- Links to latest transaction and noot
- Load Configuration: Read config from file or create default
- Open Database: Connect to SQLite and run migrations
- Seed Database: Initialize with default chain state if empty
- Catchup: Fetch and process all transactions since last known state
- Connect WebSocket: Subscribe to new transactions in real-time
- Book Transaction: Store raw Krist transaction in database
- Parse Metadata: Extract Noot message using CommonMeta parser
- Validate Message: Check message format with Zod schemas
- Apply Contract: Execute the appropriate contract function
- Journal: Record operation metadata (sequence, timestamp, originator)
- Snapshot State: Store new chain state with hash
- Persist: Save everything in a database transaction
- Sequential Processing: Transactions processed in Krist ID order
- Immutable State: Immer ensures no accidental mutations
- Validation: Journaling contract enforces monotonic sequence/timestamp
- Error Handling: Invalid operations are logged but don't halt the chain
- State Hashing: Each state snapshot includes SHA-256 hash for verification
# Run all tests
pnpm test
# Run tests in watch mode
pnpm test --watch
# Run specific test file
pnpm test src/client.test.ts
# Generate migration from schema changes
pnpm generate
# Apply migrations
pnpm up
- krist: Official Krist JavaScript library for API and WebSocket access
- drizzle-orm: Type-safe SQL query builder
- better-sqlite3: Fast SQLite3 bindings
- zod: Runtime type validation
- immer: Immutable state updates
- winston: Logging
- async-mutex: Concurrency control
- Batch Processing: Transactions processed in batches during catchup
- Indexed Queries: Database indexes on frequently queried fields
- Mutex Protection: Catchup routine protected from concurrent execution
- Efficient Serialization: JSON blobs for complex state objects
- Validation: All inputs validated with Zod schemas
- Error Isolation: Invalid transactions don't crash the node (unless
unsafe: true
) - Deterministic Execution: No external dependencies in contract execution
The core Noot client and smart contract processor.
A modern blockchain explorer built with React Router. Provides a web interface for viewing chain statistics, transactions, tokens, and sending contract messages with preflight validation.
# Start the explorer
pnpm --filter @tmpim/noot-explorer dev
See packages/explorer/README.md for more details.
Potential areas for expansion:
- Query API: REST/GraphQL API for querying chain state
- Token Standards: Additional token types (NFTs, etc.)
- Cross-Chain: Bridge to other Krist-based networks
- Smart Contract SDK: Framework for custom contract development
- Multi-Signature: Support for multi-sig token operations
MIT
Created by tmpim