Skip to Content
DevelopersNTSQuick Reference

Quick Reference

️⚠️
NTS is in active development and currently unstable. It is open to select early developers for exploration but is not yet suitable for production use. Follow developer updates for the latest changes, as performance is not at production level.

Quick reference for common N1TS patterns, functions, and best practices.

Basic App Structure

src/basic-app.ts
import { NApp, NMap, createExecutableFunctions, nmint, ntransfer, nmintEdit } from '@n1xyz/nts-compiler'; class MyApp extends NApp { // Property declarations (cannot initialize here!) counter: number; isActive: boolean; // Typed collections (initialized directly) users: NMap<UserProfile> = new NMap<UserProfile>(this, 'users'); balances: NMap<string> = new NMap<string>(this, 'balances'); // Initialize regular properties in init() method init(): void { this.counter = 0; this.isActive = true; } // Public methods (exported as transaction actions) doSomething(param: string): void { // Your logic here this.log('Action performed:', param); // Note: Transaction actions cannot return values } // Private methods (not exported) private validateInput(input: string): void { if (!input) throw new Error('Input required'); } } // Export public methods export const { doSomething } = createExecutableFunctions(MyApp);

Context Access

// Get transaction signer const user = this.signer(); // Get app admin const admin = this.appAdmin(); // Get current app ID const appId = this.appId();

Token Operations Cheat Sheet

🚫

Critical Issues with N1TS:

  1. Transaction Actions Cannot Return Values: When using functions as transaction actions, they cannot have return values and should not use await.

  2. Cannot Initialize Class Properties Directly: You MUST declare properties and initialize them in an init() method.

// ❌ Wrong class MyApp extends NApp { counter: number = 0; // This doesn't work! } // ✅ Correct class MyApp extends NApp { counter: number; init(): void { this.counter = 0; } }

Available Functions

// Import required (from @n1xyz/nts-compiler): // - nmint(totalSupply: BigInt, admin: string, meta: string): void // - ntransfer(origin: string, destination: string, mint: string, amount: BigInt): void // - nmintEdit(mint: string, freeSupply: BigInt, admin: string, meta: string): void // Globally available (no import needed): // - nread(fieldId: string, appId?: string): Promise<any> - INTERNAL USE ONLY // - nwrite(fieldId: string, value: any, tag?: string, secondary_tag?: string): void // - log(...args: any[]): void

Token Creation (Transaction Action)

// ✅ Correct: Transaction action usage createToken(): void { nmint( BigInt('1000000'), // Total supply (BigInt required) this.signer(), // Admin address 'Token Name' // Metadata (string or JSON) ); this.log('Token creation initiated'); }

Token Transfers (Transaction Actions)

// ✅ Correct: Basic transfer action transferTokens(recipient: string, mintId: string, amount: string): void { ntransfer( this.signer(), // From recipient, // To mintId, // Token ID BigInt(amount) // Amount (BigInt required) ); this.log('Transfer initiated'); }

Token Administration

// Edit token properties (admin only) nmintEdit( 'mint_id', // Token ID BigInt('500000'), // New free supply (BigInt required) 'new_admin_addr', // New admin address 'Updated metadata' // New metadata );

Reading Token Data

// Get token balance (using helper function format) const balance = await nread(`_balance_${mintId}_${address}`); // Get token admin const admin = await nread(`_mint_admin_${mintId}`); // Get token metadata const metadata = await nread(`_mint_meta_${mintId}`); // Read from external app const externalData = await nread('fieldId', 'external_app_id');

Data Operations

Writing Data

// Basic write nwrite('my_field', 'my_value'); // Write with tags nwrite('user_score', 100, 'game_data', this.signer()); // Write complex data nwrite('user_profile', JSON.stringify({ name: 'Alice', level: 5, items: ['sword', 'shield'] }), 'profile', this.signer());

Reading Data

// Read from current app const value = await nread('my_field'); // Read from specific app const appData = await nread('field_name', 'specific_app_id'); // Read from external app (requires app ID) const externalValue = await nread('field_name', 'external_app_id');

NMap Operations

class MyApp extends NApp { users: NMap<UserProfile> = new NMap<UserProfile>(this, 'users'); init(): void { // Initialize other properties here if needed } someMethod(): void { // Set data with tag this.users.set('user123', profileData, 'user_created', 'user123'); // Get data const profile = this.users.get('user123'); // Check existence if (this.users.has && this.users.has('user123')) { // User exists (if has method is available) } // Remove data this.users.delete('user123', 'user_deleted', 'user123'); } }

Access Control Patterns

Admin Only

requireAdmin(): void { if (this.signer() !== this.appAdmin()) { throw new Error('Admin access required'); } } adminFunction(): void { this.requireAdmin(); // Admin logic here }

Owner Only

// This is an internal helper method, NOT a transaction action protected async requireOwner(resourceId: string): Promise<void> { const owner = await nread(`owner_${resourceId}`); if (this.signer() !== owner) { throw new Error('Owner access required'); } }

Role-Based

class RoleBasedApp extends NApp { roles: NMap<string> = new NMap<string>(this, 'user_roles'); init(): void { // Initialize other properties here if needed } // This is an internal helper method, NOT a transaction action protected requireRole(role: string): void { const userRole = this.roles.get(this.signer()); if (userRole !== role && userRole !== 'admin') { throw new Error(`${role} access required`); } } }

Error Handling Patterns

Input Validation

validateInput(value: string, name: string): void { if (!value || value.trim().length === 0) { throw new Error(`${name} is required`); } if (value.length > 100) { throw new Error(`${name} too long (max 100 chars)`); } } validateAmount(amount: string): BigInt { let amountBig: BigInt; try { amountBig = BigInt(amount); } catch { throw new Error('Invalid amount format'); } if (amountBig <= 0) { throw new Error('Amount must be positive'); } return amountBig; }

Try-Catch Patterns

safeOperation(): void { try { // Risky operation this.performOperation(); this.log('Operation completed successfully'); } catch (error) { this.log('Operation failed:', error.message); throw error; // Re-throw to fail the transaction } }

State Management Patterns

Counters and Flags

class CounterApp extends NApp { count: number; isEnabled: boolean; lastUser: string; init(): void { this.count = 0; this.isEnabled = true; this.lastUser = ''; } increment(): void { if (!this.isEnabled) { throw new Error('Counter disabled'); } this.count++; this.lastUser = this.signer(); this.log('Counter incremented to:', this.count); } }

Collections

class CollectionsApp extends NApp { // User management users: NMap<UserProfile> = new NMap<UserProfile>(this, 'users'); userEmails: NMap<string> = new NMap<string>(this, 'emails'); // Financial data balances: NMap<string> = new NMap<string>(this, 'balances'); transactions: NMap<TransactionData> = new NMap<TransactionData>(this, 'transactions'); // Configuration settings: NMap<ConfigValue> = new NMap<ConfigValue>(this, 'settings'); permissions: NMap<boolean> = new NMap<boolean>(this, 'permissions'); init(): void { // Initialize other properties here if needed } }

Timestamps and History

class AuditableApp extends NApp { lastModified: number; history: NMap<HistoryEntry> = new NMap<HistoryEntry>(this, 'history'); data: NMap<any> = new NMap<any>(this, 'data'); init(): void { this.lastModified = 0; } updateWithHistory(key: string, value: any): void { // Save history const historyEntry: HistoryEntry = { timestamp: this.time(), user: this.signer(), action: 'update', key, oldValue: this.data.get(key), newValue: value }; this.history.set(`${key}_${this.time()}`, historyEntry, 'history', key); // Update data this.data.set(key, value, 'data_update', key); this.lastModified = this.time(); } }

Common Utility Functions

Amount Formatting

// Convert token amount with decimals parseTokenAmount(amount: string, decimals: number): BigInt { const parts = amount.split('.'); const whole = parts[0] || '0'; const fractional = (parts[1] || '').padEnd(decimals, '0').slice(0, decimals); return BigInt(whole + fractional); } // Format amount for display formatTokenAmount(amount: string, decimals: number): string { const amountBig = BigInt(amount); const divisor = BigInt(10 ** decimals); const whole = amountBig / divisor; const fractional = amountBig % divisor; return `${whole}.${fractional.toString().padStart(decimals, '0')}`; }

Address Validation

validateAddress(address: string): void { if (!address || address.length === 0) { throw new Error('Address required'); } // Add more specific validation based on your address format if (address.length < 10) { throw new Error('Invalid address format'); } }

Rate Limiting

class RateLimitedApp extends NApp { lastAction: NMap<number> = new NMap<number>(this, 'last_action'); init(): void { // Initialize other properties here if needed } checkRateLimit(user: string, cooldownSeconds: number): void { const lastTime = this.lastAction.get(user) || 0; const timeSince = this.time() - lastTime; if (timeSince < cooldownSeconds) { const timeLeft = cooldownSeconds - timeSince; throw new Error(`Please wait ${timeLeft} seconds`); } this.lastAction.set(user, this.time(), 'rate_limit', user); } }

Testing Patterns

Basic Test Setup

// tests/app.test.ts import { compileCodeAndIdl } from '@n1xyz/nts-compiler'; describe('App Tests', () => { const appCode = ` import { NApp, createExecutableFunctions } from '@n1xyz/nts-compiler'; class TestApp extends NApp { count: number; init(): void { this.count = 0; } increment(): void { this.count++; this.log('Count is now:', this.count); } } export const { increment } = createExecutableFunctions(TestApp); `; test('compiles successfully', () => { const result = compileCodeAndIdl(appCode); expect(result.code).toBeDefined(); expect(result.idl).toBeDefined(); }); });

Best Practices Checklist

Quick Checklist for N1TS Development

✅ Use BigInt for all token amounts
✅ Validate all user inputs
✅ Check permissions before state changes
✅ Use meaningful tags for NMap operations
✅ Log important events and errors
✅ Handle errors gracefully with try-catch
✅ Test your code before deployment
✅ Use descriptive variable and method names
✅ Keep methods focused and single-purpose
✅ Document complex business logic

Available Global Functions

// All these functions are globally available in N1TS apps: // Data operations await nread(fieldId: string, appId?: string): Promise<any> await nread(fieldId: string, appId: string): Promise<any> nwrite(fieldId: string, value: any, tag?: string, secondary_tag?: string): void // Token operations (transaction actions - no return values) nmint(totalSupply: BigInt, admin: string, meta: string): void nmintEdit(mint: string, freeSupply: BigInt, admin: string, meta: string): void ntransfer(origin: string, destination: string, mint: string, amount: BigInt, args?: string): void ntransferToCluster(origin: string, clusterId: string, mint: string, amount: BigInt, args?: string): void // Logging log(...args: any[]): void // Context methods (available on NApp instances) this.signer(): string this.appAdmin(): string this.appId(): string this.time(): number

Data Types Reference

// Common interfaces interface UserProfile { name: string; email: string; createdAt: number; active: boolean; } interface TransactionData { from: string; to: string; amount: string; timestamp: number; memo?: string; } interface ConfigValue { value: string | number | boolean; updatedAt: number; updatedBy: string; } interface HistoryEntry { timestamp: number; user: string; action: string; key: string; oldValue: any; newValue: any; }

This quick reference covers the most commonly used patterns in N1TS development. Keep this handy while building your smart contracts!

Last updated on