@inspectMessage

Called before every @update method. Can reject calls.

  • State: read-only
  • Replication: none
  • Async: no
  • Instruction limit: 200_000_000

Only one @inspectMessage method is allowed per canister.

Basic Usage

import { IDL, inspectMessage, msgCaller, update } from 'azle';

export default class {
    owner: string = 'owner-principal-id';

    @inspectMessage()
    inspect(methodName: string): boolean {
        const caller = msgCaller();

        // Only allow owner to call sensitive methods
        if (methodName === 'sensitiveMethod') {
            return caller.toText() === this.owner;
        }

        return true; // Allow all other methods
    }

    @update([], IDL.Text)
    sensitiveMethod(): string {
        return 'Secret data';
    }

    @update([IDL.Text], IDL.Text)
    publicMethod(message: string): string {
        return `Public: ${message}`;
    }
}

Role-Based Access Control

import { IDL, inspectMessage, msgCaller, update } from 'azle';

export default class {
    admins: Set<string> = new Set(['admin-principal-1', 'admin-principal-2']);
    moderators: Set<string> = new Set(['mod-principal-1']);

    @inspectMessage()
    checkPermissions(methodName: string): boolean {
        const caller = msgCaller().toText();

        // Admin-only methods
        if (['deleteUser', 'systemReset'].includes(methodName)) {
            return this.admins.has(caller);
        }

        // Moderator or admin methods
        if (['banUser', 'deletePost'].includes(methodName)) {
            return this.admins.has(caller) || this.moderators.has(caller);
        }

        // Public methods - all users allowed
        return true;
    }

    @update([IDL.Text], IDL.Bool)
    deleteUser(userId: string): boolean {
        // Admin only - checked in inspectMessage
        return true;
    }

    @update([IDL.Text], IDL.Bool)
    banUser(userId: string): boolean {
        // Admin or moderator - checked in inspectMessage
        return true;
    }

    @update([IDL.Text], IDL.Text)
    createPost(content: string): string {
        // Public method - all users allowed
        return `Post created: ${content}`;
    }
}

Rate Limiting

import { IDL, inspectMessage, msgCaller, update, time } from 'azle';

export default class {
    lastCallTime: Map<string, bigint> = new Map();
    rateLimitSeconds: bigint = 60n * 1_000_000_000n; // 60 seconds in nanoseconds

    @inspectMessage()
    rateLimit(methodName: string): boolean {
        const caller = msgCaller().toText();
        const now = time();

        // Only rate limit certain methods
        if (['expensiveOperation', 'sendEmail'].includes(methodName)) {
            const lastCall = this.lastCallTime.get(caller);

            if (lastCall && now - lastCall < this.rateLimitSeconds) {
                console.log(`Rate limit exceeded for ${caller}`);
                return false; // Reject the call
            }

            this.lastCallTime.set(caller, now);
        }

        return true;
    }

    @update([IDL.Text], IDL.Text)
    expensiveOperation(data: string): string {
        // Rate limited operation
        return `Processed: ${data}`;
    }

    @update([IDL.Text], IDL.Bool)
    sendEmail(recipient: string): boolean {
        // Rate limited operation
        return true;
    }
}

Method Arguments Access

import { IDL, inspectMessage, update } from 'azle';

export default class {
    @inspectMessage()
    validateArguments(methodName: string, ...args: unknown[]): boolean {
        console.log(`Method: ${methodName}, Args:`, args);

        // Validate specific method arguments
        if (methodName === 'transfer') {
            const [amount] = args as [number];
            if (amount <= 0 || amount > 1000000) {
                console.log('Invalid transfer amount');
                return false;
            }
        }

        if (methodName === 'setUsername') {
            const [username] = args as [string];
            if (username.length < 3 || username.length > 20) {
                console.log('Invalid username length');
                return false;
            }
        }

        return true;
    }

    @update([IDL.Nat], IDL.Bool)
    transfer(amount: number): boolean {
        return true;
    }

    @update([IDL.Text], IDL.Bool)
    setUsername(username: string): boolean {
        return true;
    }
}

Options

  • manual: Manual argument handling
import { IDL, inspectMessage, msgArgData, candidDecode } from 'azle';

export default class {
    @inspectMessage([], { manual: true })
    manualInspect(): boolean {
        const args = msgArgData();
        const decoded = candidDecode([IDL.Text], args);

        console.log('Manual inspect with args:', decoded);
        return true;
    }
}