IC API

The IC API is exposed as functions exported from azle. These functions provide access to Internet Computer platform capabilities.

Quick Example

import {
    msgCaller,
    time,
    canisterCycleBalance,
    call,
    IDL,
    query,
    update
} from 'azle';

export default class {
    @query([], IDL.Text)
    whoCalledWhen(): string {
        const caller = msgCaller().toText();
        const now = time();
        return `Called by ${caller} at ${now}`;
    }

    @query([], IDL.Nat)
    getBalance(): bigint {
        return canisterCycleBalance();
    }

    @update([IDL.Principal, IDL.Text], IDL.Text)
    async callOther(canisterId: Principal, message: string): Promise<string> {
        const result = await call(canisterId, 'echo', {
            args: [message],
            paramIdlTypes: [IDL.Text],
            returnIdlType: IDL.Text
        });
        return result;
    }
}

IC API Functions

Message Information

Time

  • time - Get the current Internet Computer time

Timers

Cycles

Inter-Canister Calls

  • call - Make calls to other canisters

Canister Information

Error Handling

  • trap - Terminate execution with an error
  • msgRejectCode - Get rejection code from failed calls
  • msgRejectMsg - Get rejection message from failed calls

Manual Response

  • msgReply - Reply with raw Candid-encoded data
  • msgReject - Reject with an error message

Random

  • randSeed - Seed the random number generator

Advanced

API Reference Table

Function Category Use Case
call Calls Inter-canister communication
candidDecode Advanced Manual deserialization
candidEncode Advanced Manual serialization
canisterCycleBalance Cycles Resource monitoring
canisterSelf Info Self-reference
canisterVersion Info Version tracking
chunk Advanced Memory management
clearTimer Timers Cancel scheduled operations
cyclesBurn Cycles Deflationary mechanics
dataCertificate Advanced Query verification
inReplicatedExecution Advanced Environment detection
isController Info Admin access control
msgArgData Message Manual argument processing
msgCaller Message Authentication, access control
msgCyclesAccept Cycles Payment processing
msgCyclesAvailable Cycles Payment validation
msgCyclesRefunded Cycles Cost tracking
msgMethodName Message Logging, analytics
msgReject Manual Custom error responses
msgRejectCode Errors Error classification
msgRejectMsg Errors Detailed error info
msgReply Manual Custom response handling
performanceCounter Advanced Performance monitoring
randSeed Random Secure randomness
setTimer Timers Delayed operations
setTimerInterval Timers Recurring tasks
time Time Timestamps, expiration
trap Errors Input validation

For detailed examples and usage patterns, click on any function name above to view its dedicated documentation page.

Message Information

msgCaller

Get the principal of the identity that initiated the current call:

import { msgCaller, IDL, query } from 'azle';

export default class {
    @query([], IDL.Text)
    whoAmI(): string {
        return msgCaller().toText();
    }

    @query([], IDL.Bool)
    isAnonymous(): boolean {
        return msgCaller().toText() === '2vxsx-fae';
    }
}

msgMethodName

Get the name of the currently executing method:

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

export default class {
    @update([], IDL.Text)
    currentMethod(): string {
        return msgMethodName(); // Returns "currentMethod"
    }
}

Time

time

Get the current ICP system time in nanoseconds:

import { time, IDL, query } from 'azle';

export default class {
    @query([], IDL.Nat64)
    getCurrentTime(): bigint {
        return time();
    }

    @query([], IDL.Text)
    getFormattedTime(): string {
        const nanos = time();
        const date = new Date(Number(nanos / 1_000_000n));
        return date.toISOString();
    }
}

Timers

setTimer

Execute a callback after a delay:

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

export default class {
    @update([IDL.Nat], IDL.Nat64)
    scheduleTask(delaySeconds: number): bigint {
        const timerId = setTimer(delaySeconds, () => {
            console.log('Timer executed!');
        });

        return timerId;
    }
}

setTimerInterval

Execute a callback repeatedly:

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

export default class {
    counter: number = 0;

    @update([IDL.Nat], IDL.Nat64)
    startPeriodicTask(intervalSeconds: number): bigint {
        const timerId = setTimerInterval(intervalSeconds, () => {
            this.counter += 1;
            console.log(`Periodic task executed ${this.counter} times`);
        });

        return timerId;
    }
}

clearTimer

Cancel a scheduled timer:

import { setTimer, clearTimer, IDL, update } from 'azle';

export default class {
    activeTimers: Set<bigint> = new Set();

    @update([IDL.Nat], IDL.Nat64)
    scheduleTask(delaySeconds: number): bigint {
        const timerId = setTimer(delaySeconds, () => {
            console.log('Task executed!');
            this.activeTimers.delete(timerId);
        });

        this.activeTimers.add(timerId);
        return timerId;
    }

    @update([IDL.Nat64], IDL.Bool)
    cancelTask(timerId: bigint): boolean {
        if (this.activeTimers.has(timerId)) {
            clearTimer(timerId);
            this.activeTimers.delete(timerId);
            return true;
        }
        return false;
    }
}

Cycles

canisterCycleBalance

Get the canister's current cycle balance:

import { canisterCycleBalance, IDL, query } from 'azle';

export default class {
    @query([], IDL.Nat)
    getBalance(): bigint {
        return canisterCycleBalance();
    }

    @query([], IDL.Bool)
    hasEnoughCycles(): boolean {
        const balance = canisterCycleBalance();
        const minimumRequired = 1_000_000_000n; // 1 billion cycles
        return balance >= minimumRequired;
    }
}

msgCyclesAccept

Accept cycles sent with the current call:

import { msgCyclesAccept, msgCyclesAvailable, IDL, update } from 'azle';

export default class {
    @update([], IDL.Nat)
    acceptPayment(): bigint {
        const available = msgCyclesAvailable();
        const accepted = msgCyclesAccept(available);
        return accepted;
    }
}

Inter-Canister Calls

call

Make calls to other canisters:

import { call, IDL, update, Principal } from 'azle';

export default class {
    @update([IDL.Principal, IDL.Text], IDL.Text)
    async callOtherCanister(
        canisterId: Principal,
        message: string
    ): Promise<string> {
        const result = await call(canisterId, 'process_message', {
            args: [message],
            paramIdlTypes: [IDL.Text],
            returnIdlType: IDL.Text
        });

        return result;
    }

    @update([IDL.Principal], IDL.Nat)
    async transferCycles(recipient: Principal): Promise<bigint> {
        const cyclesToSend = 1_000_000n;

        await call(recipient, 'receive_cycles', {
            cycles: cyclesToSend
        });

        return cyclesToSend;
    }
}

Canister Information

canisterSelf

Get the current canister's principal:

import { canisterSelf, IDL, query } from 'azle';

export default class {
    @query([], IDL.Principal)
    myId(): Principal {
        return canisterSelf();
    }
}

canisterVersion

Get the current canister version:

import { canisterVersion, IDL, query } from 'azle';

export default class {
    @query([], IDL.Nat64)
    getVersion(): bigint {
        return canisterVersion();
    }
}

Error Handling

trap

Terminate execution with an error:

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

export default class {
    @update([IDL.Text], IDL.Text)
    processInput(input: string): string {
        if (input === '') {
            trap('Input cannot be empty');
        }

        return `Processed: ${input}`;
    }
}

Manual Response Handling

msgReply / msgReject

For manual response handling in decorators with manual: true:

import {
    msgReply,
    msgReject,
    msgArgData,
    IDL,
    update,
    candidDecode,
    candidEncode
} from 'azle';

export default class {
    @update([], IDL.Text, { manual: true })
    manualResponse(): void {
        try {
            const result = 'Success!';
            const encoded = candidEncode([IDL.Text], [result]);
            msgReply(encoded);
        } catch (error) {
            msgReject(`Error: ${error}`);
        }
    }
}

Random Numbers

randSeed

Get a random seed for pseudorandom number generation:

import { randSeed, IDL, query } from 'azle';

export default class {
    @query([], IDL.Nat)
    getRandomNumber(): number {
        const seed = randSeed();
        // Use seed for pseudorandom number generation
        return Math.abs(seed.reduce((a, b) => a + b, 0));
    }
}

Data Structures

StableBTreeMap

A persistent B-tree map backed by stable memory that automatically persists across canister upgrades:

import { StableBTreeMap, IDL, query, update } from 'azle';

export default class {
    // Create a stable map with memory ID 0
    userProfiles = new StableBTreeMap<string, { name: string; age: number }>(0);

    // Create multiple maps with different memory IDs
    counters = new StableBTreeMap<string, bigint>(1);
    settings = new StableBTreeMap<string, boolean>(2);

    @update(
        [IDL.Text, IDL.Record({ name: IDL.Text, age: IDL.Nat })],
        IDL.Opt(IDL.Record({ name: IDL.Text, age: IDL.Nat }))
    )
    setUserProfile(
        userId: string,
        profile: { name: string; age: number }
    ): { name: string; age: number } | undefined {
        return this.userProfiles.insert(userId, profile);
    }

    @query([IDL.Text], IDL.Opt(IDL.Record({ name: IDL.Text, age: IDL.Nat })))
    getUserProfile(userId: string): { name: string; age: number } | undefined {
        return this.userProfiles.get(userId);
    }

    @query(
        [],
        IDL.Vec(
            IDL.Tuple(IDL.Text, IDL.Record({ name: IDL.Text, age: IDL.Nat }))
        )
    )
    getAllProfiles(): [string, { name: string; age: number }][] {
        return this.userProfiles.items();
    }

    @update([IDL.Text], IDL.Opt(IDL.Record({ name: IDL.Text, age: IDL.Nat })))
    removeUser(userId: string): { name: string; age: number } | undefined {
        return this.userProfiles.remove(userId);
    }

    @query([], IDL.Nat32)
    getUserCount(): number {
        return this.userProfiles.len();
    }

    @query([IDL.Text], IDL.Bool)
    userExists(userId: string): boolean {
        return this.userProfiles.containsKey(userId);
    }
}

StableBTreeMap Constructor

new StableBTreeMap<Key, Value>(
    memoryId: number,
    keySerializable?: Serializable,
    valueSerializable?: Serializable
)
  • memoryId - Unique identifier (0-253) for this map's stable memory
  • keySerializable - Optional custom serialization for keys (defaults to ICP-enabled JSON)
  • valueSerializable - Optional custom serialization for values (defaults to ICP-enabled JSON)

StableBTreeMap Methods

  • containsKey(key) - Check if a key exists
  • get(key) - Retrieve value by key
  • insert(key, value) - Insert/update key-value pair
  • remove(key) - Remove key and return its value
  • isEmpty() - Check if map is empty
  • len() - Get number of key-value pairs
  • keys(startIndex?, length?) - Get keys in sorted order
  • values(startIndex?, length?) - Get values in sorted order
  • items(startIndex?, length?) - Get key-value pairs in sorted order

Custom Serialization

import { StableBTreeMap, Serializable } from 'azle';

// Custom serializer for numbers as little-endian bytes
const numberSerializer: Serializable = {
    toBytes: (num: number) => {
        const buffer = new ArrayBuffer(8);
        const view = new DataView(buffer);
        view.setFloat64(0, num, true); // little-endian
        return new Uint8Array(buffer);
    },
    fromBytes: (bytes: Uint8Array) => {
        const view = new DataView(bytes.buffer);
        return view.getFloat64(0, true); // little-endian
    }
};

export default class {
    // Map with custom number serialization for keys
    numericMap = new StableBTreeMap<number, string>(0, numberSerializer);
}

JSON Utilities

jsonStringify / jsonParse

ICP-enabled JSON utilities that handle special types like Principal, BigInt, and Uint8Array:

import { jsonStringify, jsonParse, Principal, IDL, query, update } from 'azle';

export default class {
    @update([IDL.Text], IDL.Text)
    processComplexData(input: string): string {
        // Parse ICP-enabled JSON
        const data = jsonParse(input);

        // Work with the data
        if (data.principal) {
            data.lastAccessed = time();
            data.accessCount = (data.accessCount || 0n) + 1n;
        }

        // Convert back to ICP-enabled JSON
        return jsonStringify(data);
    }

    @query([], IDL.Text)
    getExampleData(): string {
        const complexData = {
            principal: Principal.fromText('rdmx6-jaaaa-aaaah-qcaiq-cai'),
            balance: 123_456_789n, // BigInt
            buffer: new Uint8Array([1, 2, 3, 4]),
            metadata: {
                created: time(),
                isActive: true,
                tags: ['user', 'verified']
            }
        };

        return jsonStringify(complexData);
    }

    @update([IDL.Text], IDL.Principal)
    extractPrincipal(jsonData: string): Principal {
        const parsed = jsonParse(jsonData);
        return parsed.principal; // Automatically converted back to Principal
    }
}

Supported Special Types

The ICP-enabled JSON utilities automatically handle:

  • Principal - Converted to/from text representation
  • BigInt - Converted to/from string with special markers
  • Uint8Array - Converted to/from array representation
  • undefined - Properly preserved (standard JSON loses undefined values)

Custom JSON Processing

import { jsonStringify, jsonParse } from 'azle';

// Custom replacer function
function customReplacer(key: string, value: any): any {
    if (value instanceof Date) {
        return { __date__: value.toISOString() };
    }
    return value;
}

// Custom reviver function
function customReviver(key: string, value: any): any {
    if (value?.__date__) {
        return new Date(value.__date__);
    }
    return value;
}

export default class {
    @update([IDL.Text], IDL.Text)
    processWithCustomJSON(input: string): string {
        const data = jsonParse(input, customReviver);
        data.processedAt = new Date();
        return jsonStringify(data, customReplacer);
    }
}