From 64b91bb9e986b064c42025ab34481190e7303fce Mon Sep 17 00:00:00 2001
From: Dan Steren
Date: Mon, 2 Oct 2023 00:08:28 -0600
Subject: [PATCH 01/10] Start updating docs to v0.18.0
---
the_azle_book/src/candid.md | 790 +++++++++++++---------------
the_azle_book/src/hello_world.md | 42 +-
the_azle_book/src/installation.md | 6 +-
the_azle_book/src/query_methods.md | 43 +-
the_azle_book/src/update_methods.md | 190 +++----
5 files changed, 523 insertions(+), 548 deletions(-)
diff --git a/the_azle_book/src/candid.md b/the_azle_book/src/candid.md
index de0b2c11dd..fd8b8965f9 100644
--- a/the_azle_book/src/candid.md
+++ b/the_azle_book/src/candid.md
@@ -40,112 +40,112 @@ The following is a simple example showing how to import and use most of the Cand
```typescript
import {
+ Canister,
+ Func,
+ None,
+ Null,
+ Opt,
+ Principal,
+ Record,
+ Recursive,
+ Variant,
+ Vec,
blob,
- CallResult,
- float64,
+ bool,
float32,
- Func,
+ float64,
int,
- int8,
int16,
int32,
int64,
+ int8,
nat,
- nat8,
nat16,
nat32,
nat64,
- Opt,
- Principal,
- $query,
- Query,
- Record,
- Service,
- serviceQuery,
- serviceUpdate,
- Variant,
- Vec
+ nat8,
+ query,
+ text,
+ update
} from 'azle';
-type Candid = Record<{
- text: string;
- blob: blob;
- nat: nat;
- nat64: nat64;
- nat32: nat32;
- nat16: nat16;
- nat8: nat8;
- int: int;
- int64: int64;
- int32: int32;
- int16: int16;
- int8: int8;
- float64: float64;
- float32: float32;
- bool: boolean;
- null: null;
- vec: Vec;
- opt: Opt;
- record: Record<{
- firstName: string;
- lastName: string;
- age: nat8;
- }>;
- variant: Variant<{
- Tag1: null;
- Tag2: null;
- Tag3: int;
- }>;
- func: Func Candid>>;
- service: MyService;
- principal: Principal;
-}>;
-
-class MyService extends Service {
- @serviceQuery
- query1: () => CallResult;
-
- @serviceUpdate
- update1: () => CallResult;
-}
-
-$query;
-export function candidTypes(): Candid {
- return {
- text: 'text',
- blob: Uint8Array.from([]),
- nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n,
- nat64: 18_446_744_073_709_551_615n,
- nat32: 4_294_967_295,
- nat16: 65_535,
- nat8: 255,
- int: 170_141_183_460_469_231_731_687_303_715_884_105_727n,
- int64: 9_223_372_036_854_775_807n,
- int32: 2_147_483_647,
- int16: 32_767,
- int8: 127,
- float64: Math.E,
- float32: Math.PI,
- bool: true,
- null: null,
- vec: ['has one element'],
- opt: Opt.None,
- record: {
- firstName: 'John',
- lastName: 'Doe',
- age: 35
- },
- variant: {
- Tag1: null
- },
- func: [
- Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
- 'candidTypes'
- ],
- service: new MyService(Principal.fromText('aaaaa-aa')),
- principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
- };
-}
+const MyCanister = Canister({
+ query1: query([], bool),
+ update1: update([], text)
+});
+
+const Candid = Record({
+ text: text,
+ blob: blob,
+ nat: nat,
+ nat64: nat64,
+ nat32: nat32,
+ nat16: nat16,
+ nat8: nat8,
+ int: int,
+ int64: int64,
+ int32: int32,
+ int16: int16,
+ int8: int8,
+ float64: float64,
+ float32: float32,
+ bool: bool,
+ null: Null,
+ vec: Vec(text),
+ opt: Opt(nat),
+ record: Record({
+ firstName: text,
+ lastName: text,
+ age: nat8
+ }),
+ variant: Variant({
+ Tag1: Null,
+ Tag2: Null,
+ Tag3: int
+ }),
+ func: Recursive(() => Func([], Candid, 'query')),
+ canister: MyCanister,
+ principal: Principal
+});
+
+export default Canister({
+ candidTypes: query([], Candid, () => {
+ return {
+ text: 'text',
+ blob: Uint8Array.from([]),
+ nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n,
+ nat64: 18_446_744_073_709_551_615n,
+ nat32: 4_294_967_295,
+ nat16: 65_535,
+ nat8: 255,
+ int: 170_141_183_460_469_231_731_687_303_715_884_105_727n,
+ int64: 9_223_372_036_854_775_807n,
+ int32: 2_147_483_647,
+ int16: 32_767,
+ int8: 127,
+ float64: Math.E,
+ float32: Math.PI,
+ bool: true,
+ null: null,
+ vec: ['has one element'],
+ opt: None,
+ record: {
+ firstName: 'John',
+ lastName: 'Doe',
+ age: 35
+ },
+ variant: {
+ Tag1: null
+ },
+ func: [
+ Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
+ 'candidTypes'
+ ],
+ canister: MyCanister(Principal.fromText('aaaaa-aa')),
+ principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
+ };
+ })
+});
```
Calling `candidTypes` with `dfx` will return:
@@ -187,18 +187,17 @@ The TypeScript type `string` and the Azle type `text` both correspond to the [Ca
TypeScript:
```typescript
-import { $query } from 'azle';
+import { Canister, query, text } from 'azle';
-$query;
-export function getString(): string {
- return 'Hello world!';
-}
-
-$query;
-export function printString(string: string): string {
- console.log(typeof string);
- return string;
-}
+export default Canister({
+ getString: query([], text, () => {
+ return 'Hello world!';
+ }),
+ printString: query([text], text, (string) => {
+ console.log(typeof string);
+ return string;
+ })
+});
```
Candid:
@@ -224,18 +223,17 @@ The Azle type `blob` corresponds to the [Candid type blob](https://internetcompu
TypeScript:
```typescript
-import { blob, $query } from 'azle';
+import { Canister, blob, query } from 'azle';
-$query;
-export function getBlob(): blob {
- return Uint8Array.from([68, 73, 68, 76, 0, 0]);
-}
-
-$query;
-export function printBlob(blob: blob): blob {
- console.log(typeof blob);
- return blob;
-}
+export default Canister({
+ getBlob: query([], blob, () => {
+ return Uint8Array.from([68, 73, 68, 76, 0, 0]);
+ }),
+ printBlob: query([blob], blob, (blob) => {
+ console.log(typeof blob);
+ return blob;
+ })
+});
```
Candid:
@@ -264,18 +262,17 @@ The Azle type `nat` corresponds to the [Candid type nat](https://internetcompute
TypeScript:
```typescript
-import { nat, $query } from 'azle';
+import { Canister, nat, query } from 'azle';
-$query;
-export function getNat(): nat {
- return 340_282_366_920_938_463_463_374_607_431_768_211_455n;
-}
-
-$query;
-export function printNat(nat: nat): nat {
- console.log(typeof nat);
- return nat;
-}
+export default Canister({
+ getNat: query([], nat, () => {
+ return 340_282_366_920_938_463_463_374_607_431_768_211_455n;
+ }),
+ printNat: query([nat], nat, (nat) => {
+ console.log(typeof nat);
+ return nat;
+ })
+});
```
Candid:
@@ -301,18 +298,17 @@ The Azle type `nat64` corresponds to the [Candid type nat64](https://internetcom
TypeScript:
```typescript
-import { nat64, $query } from 'azle';
+import { Canister, nat64, query } from 'azle';
-$query;
-export function getNat64(): nat64 {
- return 18_446_744_073_709_551_615n;
-}
-
-$query;
-export function printNat64(nat64: nat64): nat64 {
- console.log(typeof nat64);
- return nat64;
-}
+export default Canister({
+ getNat64: query([], nat64, () => {
+ return 18_446_744_073_709_551_615n;
+ }),
+ printNat64: query([nat64], nat64, (nat64) => {
+ console.log(typeof nat64);
+ return nat64;
+ })
+});
```
Candid:
@@ -338,18 +334,17 @@ The Azle type `nat32` corresponds to the [Candid type nat32](https://internetcom
TypeScript:
```typescript
-import { nat32, $query } from 'azle';
+import { Canister, nat32, query } from 'azle';
-$query;
-export function getNat32(): nat32 {
- return 4_294_967_295;
-}
-
-$query;
-export function printNat32(nat32: nat32): nat32 {
- console.log(typeof nat32);
- return nat32;
-}
+export default Canister({
+ getNat32: query([], nat32, () => {
+ return 4_294_967_295;
+ }),
+ printNat32: query([nat32], nat32, (nat32) => {
+ console.log(typeof nat32);
+ return nat32;
+ })
+});
```
Candid:
@@ -375,18 +370,17 @@ The Azle type `nat16` corresponds to the [Candid type nat16](https://internetcom
TypeScript:
```typescript
-import { nat16, $query } from 'azle';
-
-$query;
-export function getNat16(): nat16 {
- return 65_535;
-}
+import { Canister, nat16, query } from 'azle';
-$query;
-export function printNat16(nat16: nat16): nat16 {
- console.log(typeof nat16);
- return nat16;
-}
+export default Canister({
+ getNat16: query([], nat16, () => {
+ return 65_535;
+ }),
+ printNat16: query([nat16], nat16, (nat16) => {
+ console.log(typeof nat16);
+ return nat16;
+ })
+});
```
Candid:
@@ -412,18 +406,17 @@ The Azle type `nat8` corresponds to the [Candid type nat8](https://internetcompu
TypeScript:
```typescript
-import { nat8, $query } from 'azle';
+import { Canister, nat8, query } from 'azle';
-$query;
-export function getNat8(): nat8 {
- return 255;
-}
-
-$query;
-export function printNat8(nat8: nat8): nat8 {
- console.log(typeof nat8);
- return nat8;
-}
+export default Canister({
+ getNat8: query([], nat8, () => {
+ return 255;
+ }),
+ printNat8: query([nat8], nat8, (nat8) => {
+ console.log(typeof nat8);
+ return nat8;
+ })
+});
```
Candid:
@@ -449,18 +442,17 @@ The Azle type `int` corresponds to the [Candid type int](https://internetcompute
TypeScript:
```typescript
-import { int, $query } from 'azle';
-
-$query;
-export function getInt(): int {
- return 170_141_183_460_469_231_731_687_303_715_884_105_727n;
-}
+import { Canister, int, query } from 'azle';
-$query;
-export function printInt(int: int): int {
- console.log(typeof int);
- return int;
-}
+export default Canister({
+ getInt: query([], int, () => {
+ return 170_141_183_460_469_231_731_687_303_715_884_105_727n;
+ }),
+ printInt: query([int], int, (int) => {
+ console.log(typeof int);
+ return int;
+ })
+});
```
Candid:
@@ -486,18 +478,17 @@ The Azle type `int64` corresponds to the [Candid type int64](https://internetcom
TypeScript:
```typescript
-import { int64, $query } from 'azle';
-
-$query;
-export function getInt64(): int64 {
- return 9_223_372_036_854_775_807n;
-}
+import { Canister, int64, query } from 'azle';
-$query;
-export function printInt64(int64: int64): int64 {
- console.log(typeof int64);
- return int64;
-}
+export default Canister({
+ getInt64: query([], int64, () => {
+ return 9_223_372_036_854_775_807n;
+ }),
+ printInt64: query([int64], int64, (int64) => {
+ console.log(typeof int64);
+ return int64;
+ })
+});
```
Candid:
@@ -523,18 +514,17 @@ The Azle type `int32` corresponds to the [Candid type int32](https://internetcom
TypeScript:
```typescript
-import { int32, $query } from 'azle';
-
-$query;
-export function getInt32(): int32 {
- return 2_147_483_647;
-}
+import { Canister, int32, query } from 'azle';
-$query;
-export function printInt32(int32: int32): int32 {
- console.log(typeof int32);
- return int32;
-}
+export default Canister({
+ getInt32: query([], int32, () => {
+ return 2_147_483_647;
+ }),
+ printInt32: query([int32], int32, (int32) => {
+ console.log(typeof int32);
+ return int32;
+ })
+});
```
Candid:
@@ -560,18 +550,17 @@ The Azle type `int16` corresponds to the [Candid type int16](https://internetcom
TypeScript:
```typescript
-import { int16, $query } from 'azle';
-
-$query;
-export function getInt16(): int16 {
- return 32_767;
-}
+import { Canister, int16, query } from 'azle';
-$query;
-export function printInt16(int16: int16): int16 {
- console.log(typeof int16);
- return int16;
-}
+export default Canister({
+ getInt16: query([], int16, () => {
+ return 32_767;
+ }),
+ printInt16: query([int16], int16, (int16) => {
+ console.log(typeof int16);
+ return int16;
+ })
+});
```
Candid:
@@ -597,18 +586,17 @@ The Azle type `int8` corresponds to the [Candid type int8](https://internetcompu
TypeScript:
```typescript
-import { int8, $query } from 'azle';
-
-$query;
-export function getInt8(): int8 {
- return 127;
-}
+import { Canister, int8, query } from 'azle';
-$query;
-export function printInt8(int8: int8): int8 {
- console.log(typeof int8);
- return int8;
-}
+export default Canister({
+ getInt8: query([], int8, () => {
+ return 127;
+ }),
+ printInt8: query([int8], int8, (int8) => {
+ console.log(typeof int8);
+ return int8;
+ })
+});
```
Candid:
@@ -634,18 +622,17 @@ The Azle type `float64` and the TypeScript type `number` both correspond to the
TypeScript:
```typescript
-import { float64, $query } from 'azle';
-
-$query;
-export function getFloat64(): float64 {
- return Math.E;
-}
+import { Canister, float64, query } from 'azle';
-$query;
-export function printFloat64(float64: float64): float64 {
- console.log(typeof float64);
- return float64;
-}
+export default Canister({
+ getFloat64: query([], float64, () => {
+ return Math.E;
+ }),
+ printFloat64: query([float64], float64, (float64) => {
+ console.log(typeof float64);
+ return float64;
+ })
+});
```
Candid:
@@ -671,18 +658,17 @@ The Azle type `float32` corresponds to the [Candid type float32](https://interne
TypeScript:
```typescript
-import { float32, $query } from 'azle';
-
-$query;
-export function getFloat32(): float32 {
- return Math.PI;
-}
+import { Canister, float32, query } from 'azle';
-$query;
-export function printFloat32(float32: float32): float32 {
- console.log(typeof float32);
- return float32;
-}
+export default Canister({
+ getFloat32: query([], float32, () => {
+ return Math.PI;
+ }),
+ printFloat32: query([float32], float32, (float32) => {
+ console.log(typeof float32);
+ return float32;
+ })
+});
```
Candid:
@@ -708,18 +694,17 @@ The TypeScript type `boolean` corresponds to the [Candid type bool](https://inte
TypeScript:
```typescript
-import { $query } from 'azle';
-
-$query;
-export function getBool(): boolean {
- return true;
-}
+import { Canister, bool, query } from 'azle';
-$query;
-export function printBool(bool: boolean): boolean {
- console.log(typeof bool);
- return bool;
-}
+export default Canister({
+ getBool: query([], bool, () => {
+ return true;
+ }),
+ printBool: query([bool], bool, (bool) => {
+ console.log(typeof bool);
+ return bool;
+ })
+});
```
Candid:
@@ -745,18 +730,17 @@ The TypeScript type `null` corresponds to the [Candid type null](https://interne
TypeScript:
```typescript
-import { $query } from 'azle';
-
-$query;
-export function getNull(): null {
- return null;
-}
+import { Canister, Null, query } from 'azle';
-$query;
-export function printNull(null_: null): null {
- console.log(typeof null_);
- return null_;
-}
+export default Canister({
+ getNull: query([], Null, () => {
+ return null;
+ }),
+ printNull: query([Null], Null, (null_) => {
+ console.log(typeof null_);
+ return null_;
+ })
+});
```
Candid:
@@ -782,18 +766,17 @@ The Azle type `Vec` corresponds to the [Candid type vec](https://internetcompute
TypeScript:
```typescript
-import { int32, $query, Vec } from 'azle';
+import { Canister, Vec, int32, query } from 'azle';
-$query;
-export function getNumbers(): Vec {
- return [0, 1, 2, 3];
-}
-
-$query;
-export function printNumbers(numbers: Vec): Vec {
- console.log(typeof numbers);
- return numbers;
-}
+export default Canister({
+ getNumbers: query([], Vec(int32), () => {
+ return [0, 1, 2, 3];
+ }),
+ printNumbers: query([Vec(int32)], Vec(int32), (numbers) => {
+ console.log(typeof numbers);
+ return numbers;
+ })
+});
```
Candid:
@@ -819,17 +802,16 @@ The Azle type `Opt` corresponds to the [Candid type opt](https://internetcompute
TypeScript:
```typescript
-import { Opt, $query } from 'azle';
+import { Canister, None, Opt, Some, bool, query } from 'azle';
-$query;
-export function getOptSome(): Opt {
- return Opt.Some(true);
-}
-
-$query;
-export function getOptNone(): Opt {
- return Opt.None;
-}
+export default Canister({
+ getOptSome: query([], Opt(bool), () => {
+ return Some(true);
+ }),
+ getOptNone: query([], Opt(bool), () => {
+ return None;
+ })
+});
```
Candid:
@@ -858,26 +840,25 @@ TypeScript type aliases referring to object literals wrapped in the `Record` Azl
TypeScript:
```typescript
-import { Principal, $query, Record } from 'azle';
-
-type User = Record<{
- id: Principal;
- username: string;
-}>;
-
-$query;
-export function getUser(): User {
- return {
- id: Principal.fromUint8Array(Uint8Array.from([0])),
- username: 'lastmjs'
- };
-}
-
-$query;
-export function printUser(user: User): User {
- console.log(typeof user);
- return user;
-}
+import { Canister, Principal, Record, query, text } from 'azle';
+
+const User = Record({
+ id: Principal,
+ username: text
+});
+
+export default Canister({
+ getUser: query([], User, () => {
+ return {
+ id: Principal.fromUint8Array(Uint8Array.from([0])),
+ username: 'lastmjs'
+ };
+ }),
+ printUser: query([User], User, (user) => {
+ console.log(typeof user);
+ return user;
+ })
+});
```
Candid:
@@ -904,32 +885,31 @@ TypeScript type aliases referring to object literals wrapped in the `Variant` Az
TypeScript:
```typescript
-import { $query, Variant } from 'azle';
-
-type Reaction = Variant<{
- Fire: null;
- ThumbsUp: null;
- Emotion: Emotion;
-}>;
-
-type Emotion = Variant<{
- Happy: null;
- Indifferent: null;
- Sad: null;
-}>;
-
-$query;
-export function getReaction(): Reaction {
- return {
- Fire: null
- };
-}
-
-$query;
-export function printReaction(reaction: Reaction): Reaction {
- console.log(typeof reaction);
- return reaction;
-}
+import { Canister, Null, Variant, query } from 'azle';
+
+const Emotion = Variant({
+ Happy: Null,
+ Indifferent: Null,
+ Sad: Null
+});
+
+const Reaction = Variant({
+ Fire: Null,
+ ThumbsUp: Null,
+ Emotion: Emotion
+});
+
+export default Canister({
+ getReaction: query([], Reaction, () => {
+ return {
+ Fire: null
+ };
+ }),
+ printReaction: query([Reaction], Reaction, (reaction) => {
+ console.log(typeof reaction);
+ return reaction;
+ })
+});
```
Candid:
@@ -961,20 +941,22 @@ A `func` acts as a callback, allowing the `func` receiver to know which canister
TypeScript:
```typescript
-import { Func, Principal, $query, Query } from 'azle';
+import { Canister, Func, Principal, query, text } from 'azle';
-type BasicFunc = Func string>>;
+const BasicFunc = Func([text], text, 'query');
-$query;
-export function getBasicFunc(): BasicFunc {
- return [Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), 'getBasicFunc'];
-}
-
-$query;
-export function printBasicFunc(basicFunc: BasicFunc): BasicFunc {
- console.log(typeof basicFunc);
- return basicFunc;
-}
+export default Canister({
+ getBasicFunc: query([], BasicFunc, () => {
+ return [
+ Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
+ 'getBasicFunc'
+ ];
+ }),
+ printBasicFunc: query([BasicFunc], BasicFunc, (basicFunc) => {
+ console.log(typeof basicFunc);
+ return basicFunc;
+ })
+});
```
Candid:
@@ -1002,36 +984,21 @@ JavaScript classes that inherit from the Azle type `Service` correspond to the [
TypeScript:
```typescript
-import {
- CallResult,
- Principal,
- $query,
- Result,
- Service,
- serviceQuery,
- serviceUpdate,
- $update
-} from 'azle';
+import { Canister, Principal, bool, ic, query, text, update } from 'azle';
-class SomeService extends Service {
- @serviceQuery
- query1: () => CallResult;
+const SomeCanister = Canister({
+ query1: query([], bool),
+ update1: update([], text)
+});
- @serviceUpdate
- update1: () => CallResult;
-}
-
-$query;
-export function getService(): SomeService {
- return new SomeService(Principal.fromText('aaaaa-aa'));
-}
-
-$update;
-export async function callService(
- service: SomeService
-): Promise> {
- return await service.update1().call();
-}
+export default Canister({
+ getService: query([], SomeCanister, () => {
+ return SomeCanister(Principal.fromText('aaaaa-aa'));
+ }),
+ callService: update([SomeCanister], text, (service) => {
+ return ic.call(service.update1);
+ })
+});
```
Candid:
@@ -1062,18 +1029,17 @@ The Azle type `Principal` corresponds to the [Candid type principal](https://int
TypeScript:
```typescript
-import { Principal, $query } from 'azle';
-
-$query;
-export function getPrincipal(): Principal {
- return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai');
-}
+import { Canister, Principal, query } from 'azle';
-$query;
-export function printPrincipal(principal: Principal): Principal {
- console.log(typeof principal);
- return principal;
-}
+export default Canister({
+ getPrincipal: query([], Principal, () => {
+ return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai');
+ }),
+ printPrincipal: query([Principal], Principal, (principal) => {
+ console.log(typeof principal);
+ return principal;
+ })
+});
```
Candid:
@@ -1099,18 +1065,17 @@ The Azle type `reserved` corresponds to the [Candid type reserved](https://inter
TypeScript:
```typescript
-import { $query, reserved } from 'azle';
-
-$query;
-export function getReserved(): reserved {
- return 'anything';
-}
+import { Canister, query, reserved } from 'azle';
-$query;
-export function printReserved(reserved: reserved): reserved {
- console.log(typeof reserved);
- return reserved;
-}
+export default Canister({
+ getReserved: query([], reserved, () => {
+ return 'anything';
+ }),
+ printReserved: query([reserved], reserved, (reserved) => {
+ console.log(typeof reserved);
+ return reserved;
+ })
+});
```
Candid:
@@ -1136,20 +1101,19 @@ The Azle type `empty` corresponds to the [Candid type empty](https://internetcom
TypeScript:
```typescript
-import { empty, $query } from 'azle';
-
-$query;
-export function getEmpty(): empty {
- throw 'Anything you want';
-}
-
-// Note: It is impossible to call this function because it requires an argument
-// but there is no way to pass an "empty" value as an argument.
-$query;
-export function printEmpty(empty: empty): empty {
- console.log(typeof empty);
- throw 'Anything you want';
-}
+import { Canister, empty, query } from 'azle';
+
+export default Canister({
+ getEmpty: query([], empty, () => {
+ throw 'Anything you want';
+ }),
+ // Note: It is impossible to call this function because it requires an argument
+ // but there is no way to pass an "empty" value as an argument.
+ printEmpty: query([empty], empty, (empty) => {
+ console.log(typeof empty);
+ throw 'Anything you want';
+ })
+});
```
Candid:
diff --git a/the_azle_book/src/hello_world.md b/the_azle_book/src/hello_world.md
index de8e8691fc..bee77c1f05 100644
--- a/the_azle_book/src/hello_world.md
+++ b/the_azle_book/src/hello_world.md
@@ -63,32 +63,31 @@ Open up `azle_hello_world` in your text editor (we recommend [VS Code](https://c
Here's the main code of the project, which you should put in the `azle_hello_world/src/index.ts` file of your canister:
```typescript
-import { $query, $update } from 'azle';
+import { Canister, Void, query, text, update } from 'azle';
// This is a global variable that is stored on the heap
let message: string = '';
-// Query calls complete quickly because they do not go through consensus
-$query;
-export function getMessage(): string {
- return message;
-}
-
-// Update calls take a few seconds to complete
-// This is because they persist state changes and go through consensus
-$update;
-export function setMessage(newMessage: string): void {
- message = newMessage; // This change will be persisted
-}
+export default Canister({
+ // Query calls complete quickly because they do not go through consensus
+ getMessage: query([], text, () => {
+ return message;
+ }),
+ // Update calls take a few seconds to complete
+ // This is because they persist state changes and go through consensus
+ setMessage: update([text], Void, (newMessage) => {
+ message = newMessage; // This change will be persisted
+ })
+});
```
Let's discuss each section of the code.
```typescript
-import { $query, $update } from 'azle';
+import { Canister, Void, query, text, update } from 'azle';
```
-The code starts off by importing the `$query` and `$update` annotations from `azle`. The `azle` module provides most of the Internet Computer (IC) APIs for your canister.
+The code starts off by importing the `query` and `update` annotations from `azle`. The `azle` module provides most of the Internet Computer (IC) APIs for your canister.
```typescript
// This is a global variable that is stored on the heap
@@ -99,10 +98,9 @@ We have created a global variable to store the state of our application. This va
```typescript
// Query calls complete quickly because they do not go through consensus
-$query;
-export function getMessage(): string {
+getMessage: query([], text, () => {
return message;
-}
+}),
```
We are exposing a canister query method here. When query methods are called they execute quickly because they do not have to go through consensus. This method simply returns our global `message` variable.
@@ -110,10 +108,9 @@ We are exposing a canister query method here. When query methods are called they
```typescript
// Update calls take a few seconds to complete
// This is because they persist state changes and go through consensus
-$update;
-export function setMessage(newMessage: string): void {
+setMessage: update([text], Void, (newMessage) => {
message = newMessage; // This change will be persisted
-}
+});
```
We are exposing an update method here. When update methods are called they take a few seconds to complete. This is because they persist changes and go through consensus. A majority of nodes in a subnet must agree on all state changes introduced in calls to update methods. This method accepts a `string` from the caller and will store it in our global `message` variable.
@@ -149,9 +146,8 @@ Create the following in `azle_hello_world/dfx.json`:
"canisters": {
"azle_hello_world": {
"type": "custom",
+ "main": "src/index.ts",
"build": "npx azle azle_hello_world",
- "root": "src",
- "ts": "src/index.ts",
"candid": "src/index.did",
"wasm": ".azle/azle_hello_world/azle_hello_world.wasm.gz"
}
diff --git a/the_azle_book/src/installation.md b/the_azle_book/src/installation.md
index b19bd60e2a..08d68afc50 100644
--- a/the_azle_book/src/installation.md
+++ b/the_azle_book/src/installation.md
@@ -5,7 +5,7 @@ Follow the instructions exactly as stated below to avoid issues.
You should be using a \*nix environment (Linux, Mac OS, [WSL if using Windows](https://learn.microsoft.com/en-us/windows/wsl/install)) with bash and have the following installed on your system:
- Node.js 18
-- dfx 0.14.2
+- dfx 0.15.0
## Node.js
@@ -23,10 +23,10 @@ nvm install 18
## dfx
-Run the following command to install dfx 0.14.2:
+Run the following command to install dfx 0.15.0:
```bash
-DFX_VERSION=0.14.2 sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
+DFX_VERSION=0.15.0 sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
```
If after trying to run `dfx` commands you encounter an error such as `dfx: command not found`, you might need to add `$HOME/bin` to your path. Here's an example of doing this in your `.bashrc`:
diff --git a/the_azle_book/src/query_methods.md b/the_azle_book/src/query_methods.md
index d5c8ba3cde..0f9ad30ebd 100644
--- a/the_azle_book/src/query_methods.md
+++ b/the_azle_book/src/query_methods.md
@@ -2,7 +2,7 @@
## TLDR
-- Annotate functions with `$query`
+- Annotate functions with `query`
- Read-only
- Executed on a single node
- No consensus
@@ -14,12 +14,13 @@
The most basic way to expose your canister's functionality publicly is through a query method. Here's an example of a simple query method:
```typescript
-import { $query } from 'azle';
+import { Canister, query, text } from 'azle';
-$query;
-export function getString(): string {
- return 'This is a query method!';
-}
+export default Canister({
+ getString: query([], text, () => {
+ return 'This is a query method!';
+ })
+});
```
`getString` can be called from the outside world through the IC's HTTP API. You'll usually invoke this API from the [`dfx command line`, `dfx web UI`, or an agent](./deployment.md#interacting-with-your-canister).
@@ -33,16 +34,17 @@ dfx canister call my_canister getString
Query methods are read-only. They do not persist any state changes. Take a look at the following example:
```typescript
-import { $query } from 'azle';
+import { Canister, Void, query, text } from 'azle';
let db: {
[key: string]: string;
} = {};
-$query;
-export function set(key: string, value: string): void {
- db[key] = value;
-}
+export default Canister({
+ set: query([text, text], Void, (key, value) => {
+ db[key] = value;
+ })
+});
```
Calling `set` will perform the operation of setting the `key` property on the `db` object to `value`, but after the call finishes that change will be discarded.
@@ -52,15 +54,16 @@ This is because query methods are executed on a single node machine and do not g
There is a limit to how much computation can be done in a single call to a query method. The current query call limit is [5 billion Wasm instructions](https://internetcomputer.org/docs/current/developer-docs/production/instruction-limits). Here's an example of a query method that runs the risk of reaching the limit:
```typescript
-import { nat32, $query } from 'azle';
-
-$query;
-export function pyramid(levels: nat32): string {
- return new Array(levels).fill(0).reduce((acc, _, index) => {
- const asterisks = new Array(index + 1).fill('*').join('');
- return `${acc}${asterisks}\n`;
- }, '');
-}
+import { Canister, nat32, query, text } from 'azle';
+
+export default Canister({
+ pyramid: query([nat32], text, (levels) => {
+ return new Array(levels).fill(0).reduce((acc, _, index) => {
+ const asterisks = new Array(index + 1).fill('*').join('');
+ return `${acc}${asterisks}\n`;
+ }, '');
+ })
+});
```
From the `dfx command line` you can call `pyramid` like this:
diff --git a/the_azle_book/src/update_methods.md b/the_azle_book/src/update_methods.md
index 98e6fe9915..13b4326f5e 100644
--- a/the_azle_book/src/update_methods.md
+++ b/the_azle_book/src/update_methods.md
@@ -2,7 +2,7 @@
## TLDR
-- Annotate functions with `$update`
+- Annotate functions with `update`
- Read-write
- Executed on many nodes
- Consensus
@@ -15,14 +15,15 @@
Update methods are similar to query methods, but state changes can be persisted. Here's an example of a simple update method:
```typescript
-import { nat64, $update } from 'azle';
+import { Canister, nat64, update } from 'azle';
let counter = 0n;
-$update;
-export function increment(): nat64 {
- return counter++;
-}
+export default Canister({
+ increment: update([], nat64, () => {
+ return counter++;
+ })
+});
```
Calling `increment` will return the current value of `counter` and then increase its value by 1. Because `counter` is a global variable, the change will be persisted to the heap, and subsequent query and update calls will have access to the new `counter` value.
@@ -32,19 +33,18 @@ Because the Internet Computer (IC) persists changes with certain fault tolerance
Due to the latency and other expenses involved with update methods, it is best to use them only when necessary. Look at the following example:
```typescript
-import { $query, $update } from 'azle';
+import { Canister, Void, query, text, update } from 'azle';
let message = '';
-$query;
-export function getMessage(): string {
- return message;
-}
-
-$update;
-export function setMessage(newMessage: string): void {
- message = newMessage;
-}
+export default Canister({
+ getMessage: query([], text, () => {
+ return message;
+ }),
+ setMessage: update([text], Void, (newMessage) => {
+ message = newMessage;
+ })
+});
```
You'll notice that we use an update method, `setMessage`, only to perform the change to the global `message` variable. We use `getMessage`, a query method, to read the message.
@@ -52,7 +52,7 @@ You'll notice that we use an update method, `setMessage`, only to perform the ch
Keep in mind that the heap is limited to 4 GiB, and thus there is an upper bound to global variable storage capacity. You can imagine how a simple database like the following would eventually run out of memory with too many entries:
```typescript
-import { Opt, $query, $update } from 'azle';
+import { Canister, None, Opt, Some, Void, query, text, update } from 'azle';
type Db = {
[key: string]: string;
@@ -60,34 +60,32 @@ type Db = {
let db: Db = {};
-$query;
-export function get(key: string): Opt {
- const value = db[key];
- return value !== undefined ? Opt.Some(value) : Opt.None;
-}
-
-$update;
-export function set(key: string, value: string): void {
- db[key] = value;
-}
+export default Canister({
+ get: query([text], Opt(text), (key) => {
+ const value = db[key];
+ return value !== undefined ? Some(value) : None;
+ }),
+ set: update([text, text], Void, (key, value) => {
+ db[key] = value;
+ })
+});
```
If you need more than 4 GiB of storage, consider taking advantage of the 48 GiB of stable memory. Stable structures like `StableBTreeMap` give you a nice API for interacting with stable memory. These data structures will be [covered in more detail later](./stable_structures.md). Here's a simple example:
```typescript
-import { Opt, $query, StableBTreeMap, $update } from 'azle';
-
-let db = new StableBTreeMap(0, 10, 10);
-
-$query;
-export function get(key: string): Opt {
- return db.get(key);
-}
-
-$update;
-export function set(key: string, value: string): void {
- db.insert(key, value);
-}
+import { Canister, Opt, StableBTreeMap, Void, query, text, update } from 'azle';
+
+let db = StableBTreeMap(text, text, 0);
+
+export default Canister({
+ get: query([text], Opt(text), (key) => {
+ return db.get(key);
+ }),
+ set: update([text, text], Void, (key, value) => {
+ db.insert(key, value);
+ })
+});
```
So far we have only seen how state changes can be persisted. State changes can also be discarded by implicit or explicit traps. A trap is an immediate stop to execution with the ability to provide a message to the execution environment.
@@ -97,35 +95,43 @@ Traps can be useful for ensuring that multiple operations are either all complet
Here's an example of how to trap and ensure atomic changes to your database:
```typescript
-import { ic, Opt, $query, Record, StableBTreeMap, $update, Vec } from 'azle';
-
-type Entry = Record<{
- key: string;
- value: string;
-}>;
-
-let db = new StableBTreeMap(0, 10, 10);
-
-$query;
-export function get(key: string): Opt {
- return db.get(key);
-}
-
-$update;
-export function set(key: string, value: string): void {
- db.insert(key, value);
-}
-
-$update;
-export function setMany(entries: Vec): void {
- entries.forEach((entry) => {
- if (entry.key === 'trap') {
- ic.trap('explicit trap');
- }
-
- db.insert(entry.key, entry.value);
- });
-}
+import {
+ Canister,
+ Opt,
+ Record,
+ StableBTreeMap,
+ Vec,
+ Void,
+ ic,
+ query,
+ text,
+ update
+} from 'azle';
+
+const Entry = Record({
+ key: text,
+ value: text
+});
+
+let db = StableBTreeMap(text, text, 0);
+
+export default Canister({
+ get: query([text], Opt(text), (key) => {
+ return db.get(key);
+ }),
+ set: update([text, text], Void, (key, value) => {
+ db.insert(key, value);
+ }),
+ setMany: update([Vec(Entry)], Void, (entries) => {
+ entries.forEach((entry) => {
+ if (entry.key === 'trap') {
+ ic.trap('explicit trap');
+ }
+
+ db.insert(entry.key, entry.value);
+ });
+ })
+});
```
In addition to `ic.trap`, an explicit JavaScript `throw` or any unhandled exception will also trap.
@@ -133,26 +139,32 @@ In addition to `ic.trap`, an explicit JavaScript `throw` or any unhandled except
There is a limit to how much computation can be done in a single call to an update method. The current update call limit is [20 billion Wasm instructions](https://internetcomputer.org/docs/current/developer-docs/production/instruction-limits). If we modify our database example, we can introduce an update method that runs the risk of reaching the limit:
```typescript
-import { nat64, Opt, $query, StableBTreeMap, $update } from 'azle';
-
-let db = new StableBTreeMap(0, 1_000, 1_000);
-
-$query;
-export function get(key: string): Opt {
- return db.get(key);
-}
-
-$update;
-export function set(key: string, value: string): void {
- db.insert(key, value);
-}
-
-$update;
-export function setMany(numEntries: nat64): void {
- for (let i = 0; i < numEntries; i++) {
- db.insert(i.toString(), i.toString());
- }
-}
+import {
+ Canister,
+ Opt,
+ StableBTreeMap,
+ Void,
+ nat64,
+ query,
+ text,
+ update
+} from 'azle';
+
+let db = StableBTreeMap(text, text, 0);
+
+export default Canister({
+ get: query([text], Opt(text), (key) => {
+ return db.get(key);
+ }),
+ set: update([text, text], Void, (key, value) => {
+ db.insert(key, value);
+ }),
+ setMany: update([nat64], Void, (numEntries) => {
+ for (let i = 0; i < numEntries; i++) {
+ db.insert(i.toString(), i.toString());
+ }
+ })
+});
```
From the `dfx command line` you can call `setMany` like this:
From fd2297a0e616cf7bbeaaf2e9a24df9b1e401045d Mon Sep 17 00:00:00 2001
From: Dan Steren
Date: Mon, 2 Oct 2023 00:29:28 -0600
Subject: [PATCH 02/10] Update chapter 12
---
the_azle_book/src/stable_structures.md | 421 +++++++++++--------------
1 file changed, 192 insertions(+), 229 deletions(-)
diff --git a/the_azle_book/src/stable_structures.md b/the_azle_book/src/stable_structures.md
index d4b0a852c0..765cea7066 100644
--- a/the_azle_book/src/stable_structures.md
+++ b/the_azle_book/src/stable_structures.md
@@ -6,8 +6,6 @@
- Persistent across upgrades
- Familiar API
- Must specify memory id
-- Must specify maximum key size
-- Must specify maximum value size
- No migrations per memory id
Stable structures are data structures with familiar APIs that allow access to stable memory. Stable memory is a separate memory location from the heap that currently allows up to 64 GiB of storage. Stable memory persists automatically across upgrades.
@@ -21,80 +19,74 @@ Azle currently provides one stable structure called `StableBTreeMap`. It's simil
Here's how to define a simple `StableBTreeMap`. Each `StableBTreeMap` must be defined in the global scope (not within any functions or objects etc):
```typescript
-import { nat8, StableBTreeMap } from 'azle';
+import { StableBTreeMap, nat8, text } from 'azle';
-let map = new StableBTreeMap(0, 100, 1_000);
+let map = StableBTreeMap(nat8, text, 0);
```
-This is a `StableBTreeMap` with a key of type `nat8` and a value of type `string`. Key and value types can be any [Candid type](candid.md).
+This is a `StableBTreeMap` with a key of type `nat8` and a value of type `text`. Key and value types can be any [Candid type](candid.md).
-This `StableBTreeMap` also has a `memory id` of `0`, a maximum key size of `100` bytes and a maximum value size of `1_000` bytes. You must statically specify the `memory id`, maximum key size, and maximum value sizes (they cannot be variables).
-
-Each `StableBTreeMap` instance must have a unique `memory id`. Once a `memory id` is allocated, it cannot be used with a different `StableBTreeMap`. This means you can't create another `StableBTreeMap` using the same `memory id`, and you can't change the key or value types of an existing `StableBTreeMap`. [This problem will be addressed](https://github.com/demergent-labs/azle/issues/843).
+This `StableBTreeMap` also has a `memory id` of `0`. Each `StableBTreeMap` instance must have a unique `memory id`. Once a `memory id` is allocated, it cannot be used with a different `StableBTreeMap`. This means you can't create another `StableBTreeMap` using the same `memory id`, and you can't change the key or value types of an existing `StableBTreeMap`. [This problem will be addressed](https://github.com/demergent-labs/azle/issues/843).
Here's an example showing all of the basic `StableBTreeMap` operations:
```typescript
import {
- nat64,
- nat8,
+ Canister,
Opt,
- $query,
StableBTreeMap,
Tuple,
- $update,
- Vec
+ Vec,
+ bool,
+ nat64,
+ nat8,
+ query,
+ text,
+ update
} from 'azle';
-type Key = nat8;
-type Value = string;
+const Key = nat8;
+const Value = text;
-let map = new StableBTreeMap(0, 100, 1_000);
+let map = StableBTreeMap(Key, Value, 0);
-$query;
-export function containsKey(key: Key): boolean {
- return map.containsKey(key);
-}
+export default Canister({
+ containsKey: query([Key], bool, (key) => {
+ return map.containsKey(key);
+ }),
-$query;
-export function get(key: Key): Opt {
- return map.get(key);
-}
+ get: query([Key], Opt(Value), (key) => {
+ return map.get(key);
+ }),
-$update;
-export function insert(key: Key, value: Value): Opt {
- return map.insert(key, value);
-}
+ insert: update([Key, Value], Opt(Value), (key, value) => {
+ return map.insert(key, value);
+ }),
-$query;
-export function isEmpty(): boolean {
- return map.isEmpty();
-}
+ isEmpty: query([], bool, () => {
+ return map.isEmpty();
+ }),
-$query;
-export function items(): Vec> {
- return map.items();
-}
+ items: query([], Vec(Tuple(Key, Value)), () => {
+ return map.items();
+ }),
-$query;
-export function keys(): Vec {
- return map.keys();
-}
+ keys: query([], Vec(Key), () => {
+ return map.keys();
+ }),
-$query;
-export function len(): nat64 {
- return map.len();
-}
+ len: query([], nat64, () => {
+ return map.len();
+ }),
-$update;
-export function remove(key: Key): Opt {
- return map.remove(key);
-}
+ remove: update([Key], Opt(Value), (key) => {
+ return map.remove(key);
+ }),
-$query;
-export function values(): Vec {
- return map.values();
-}
+ values: query([], Vec(Value), () => {
+ return map.values();
+ })
+});
```
With these basic operations you can build more complex CRUD database applications:
@@ -102,111 +94,101 @@ With these basic operations you can build more complex CRUD database application
```typescript
import {
blob,
+ Canister,
ic,
+ Err,
nat64,
+ Ok,
Opt,
Principal,
- $query,
+ query,
Record,
Result,
StableBTreeMap,
- $update,
+ text,
+ update,
Variant,
- Vec,
- match
+ Vec
} from 'azle';
-type User = Record<{
- id: Principal;
- createdAt: nat64;
- recordingIds: Vec;
- username: string;
-}>;
-
-type Recording = Record<{
- id: Principal;
- audio: blob;
- createdAt: nat64;
- name: string;
- userId: Principal;
-}>;
-
-let users = new StableBTreeMap(0, 38, 100_000);
-let recordings = new StableBTreeMap(1, 38, 5_000_000);
-
-$update;
-export function createUser(username: string): User {
- const id = generateId();
- const user: User = {
- id,
- createdAt: ic.time(),
- recordingIds: [],
- username
- };
-
- users.insert(user.id, user);
-
- return user;
-}
+const User = Record({
+ id: Principal,
+ createdAt: nat64,
+ recordingIds: Vec(Principal),
+ username: text
+});
-$query;
-export function readUsers(): Vec {
- return users.values();
-}
+const Recording = Record({
+ id: Principal,
+ audio: blob,
+ createdAt: nat64,
+ name: text,
+ userId: Principal
+});
+
+const AudioRecorderError = Variant({
+ RecordingDoesNotExist: Principal,
+ UserDoesNotExist: Principal
+});
+
+let users = StableBTreeMap(Principal, User, 0);
+let recordings = StableBTreeMap(Principal, Recording, 1);
+
+export default Canister({
+ createUser: update([text], User, (username) => {
+ const id = generateId();
+ const user: typeof User = {
+ id,
+ createdAt: ic.time(),
+ recordingIds: [],
+ username
+ };
+
+ users.insert(user.id, user);
+
+ return user;
+ }),
+ readUsers: query([], Vec(User), () => {
+ return users.values();
+ }),
+ readUserById: query([Principal], Opt(User), (id) => {
+ return users.get(id);
+ }),
+ deleteUser: update([Principal], Result(User, AudioRecorderError), (id) => {
+ const userOpt = users.get(id);
+
+ if (userOpt.length === 0) {
+ return Err({
+ UserDoesNotExist: id
+ });
+ }
-$query;
-export function readUserById(id: Principal): Opt {
- return users.get(id);
-}
+ const user = userOpt[0];
-$update;
-export function deleteUser(id: Principal): Result<
- User,
- Variant<{
- UserDoesNotExist: Principal;
- }>
-> {
- const user = users.get(id);
-
- return match(user, {
- Some: (user) => {
- user.recordingIds.forEach((recordingId) => {
- recordings.remove(recordingId);
- });
+ user.recordingIds.forEach((recordingId) => {
+ recordings.remove(recordingId);
+ });
- users.remove(user.id);
+ users.remove(user.id);
- return {
- Ok: user
- };
- },
- None: () => {
- return {
- Err: {
- UserDoesNotExist: id
- }
- };
- }
- });
-}
+ return Ok(user);
+ }),
+ createRecording: update(
+ [blob, text, Principal],
+ Result(Recording, AudioRecorderError),
+ (audio, name, userId) => {
+ const userOpt = users.get(userId);
+
+ if (userOpt.length === 0) {
+ return Err({
+ UserDoesNotExist: userId
+ });
+ }
+
+ const user = userOpt[0];
-$update;
-export function createRecording(
- audio: blob,
- name: string,
- userId: Principal
-): Result<
- Recording,
- Variant<{
- UserDoesNotExist: Principal;
- }>
-> {
- const user = users.get(userId);
-
- return match(user, {
- Some: (user) => {
const id = generateId();
- const recording: Recording = {
+ const recording: typeof Recording = {
id,
audio,
createdAt: ic.time(),
@@ -216,87 +198,60 @@ export function createRecording(
recordings.insert(recording.id, recording);
- const updatedUser: User = {
+ const updatedUser: typeof User = {
...user,
recordingIds: [...user.recordingIds, recording.id]
};
users.insert(updatedUser.id, updatedUser);
- return {
- Ok: recording
- };
- },
- None: () => {
- return {
- Err: {
- UserDoesNotExist: userId
- }
- };
+ return Ok(recording);
}
- });
-}
+ ),
+ readRecordings: query([], Vec(Recording), () => {
+ return recordings.values();
+ }),
+ readRecordingById: query([Principal], Opt(Recording), (id) => {
+ return recordings.get(id);
+ }),
+ deleteRecording: update(
+ [Principal],
+ Result(Recording, AudioRecorderError),
+ (id) => {
+ const recordingOpt = recordings.get(id);
+
+ if (recordingOpt.length === 0) {
+ return Err({ RecordingDoesNotExist: id });
+ }
+
+ const recording = recordingOpt[0];
+
+ const userOpt = users.get(recording.userId);
+
+ if (userOpt.length === 0) {
+ return Err({
+ UserDoesNotExist: recording.userId
+ });
+ }
+
+ const user = userOpt[0];
+
+ const updatedUser: typeof User = {
+ ...user,
+ recordingIds: user.recordingIds.filter(
+ (recordingId) =>
+ recordingId.toText() !== recording.id.toText()
+ )
+ };
-$query;
-export function readRecordings(): Vec {
- return recordings.values();
-}
+ users.insert(updatedUser.id, updatedUser);
-$query;
-export function readRecordingById(id: Principal): Opt {
- return recordings.get(id);
-}
+ recordings.remove(id);
-$update;
-export function deleteRecording(id: Principal): Result<
- Recording,
- Variant<{
- RecordingDoesNotExist: Principal;
- UserDoesNotExist: Principal;
- }>
-> {
- const recording = recordings.get(id);
-
- return match(recording, {
- Some: (recording) => {
- const user = users.get(recording.userId);
-
- return match(user, {
- Some: (user) => {
- const updatedUser: User = {
- ...user,
- recordingIds: user.recordingIds.filter(
- (recordingId) =>
- recordingId.toText() !== recording.id.toText()
- )
- };
-
- users.insert(updatedUser.id, updatedUser);
-
- recordings.remove(id);
-
- return {
- Ok: recording
- };
- },
- None: () => {
- return {
- Err: {
- UserDoesNotExist: recording.userId
- }
- };
- }
- });
- },
- None: () => {
- return {
- Err: {
- RecordingDoesNotExist: id
- }
- };
+ return Ok(recording);
}
- });
-}
+ )
+});
function generateId(): Principal {
const randomBytes = new Array(29)
@@ -312,25 +267,33 @@ The example above shows a very basic audio recording backend application. There
Each entity gets its own `StableBTreeMap`:
```typescript
-import { blob, nat64, Principal, Record, StableBTreeMap, Vec } from 'azle';
-
-type User = Record<{
- id: Principal;
- createdAt: nat64;
- recordingIds: Vec;
- username: string;
-}>;
-
-type Recording = Record<{
- id: Principal;
- audio: blob;
- createdAt: nat64;
- name: string;
- userId: Principal;
-}>;
-
-let users = new StableBTreeMap(0, 38, 100_000);
-let recordings = new StableBTreeMap(1, 38, 5_000_000);
+import {
+ Principal,
+ Record,
+ StableBTreeMap,
+ Vec,
+ blob,
+ nat64,
+ text
+} from 'azle';
+
+const User = Record({
+ id: Principal,
+ createdAt: nat64,
+ recordingIds: Vec(Principal),
+ username: text
+});
+
+const Recording = Record({
+ id: Principal,
+ audio: blob,
+ createdAt: nat64,
+ name: text,
+ userId: Principal
+});
+
+let users = StableBTreeMap(Principal, User, 0);
+let recordings = StableBTreeMap(Principal, Recording, 1);
```
Notice that each `StableBTreeMap` has a unique `memory id`. The maximum key and value sizes are also set according to the expected application usage.
From 330feda41765b5451f8fdae7d94a0c2f05b76e8f Mon Sep 17 00:00:00 2001
From: Jordan Last
Date: Mon, 2 Oct 2023 09:13:03 -0500
Subject: [PATCH 03/10] working on the azle book
---
README.md | 30 +-
the_azle_book/book/404.html | 2 +-
the_azle_book/book/azle.html | 33 +-
the_azle_book/book/candid.html | 834 +++++----
the_azle_book/book/canisters_overview.html | 6 +-
the_azle_book/book/cross_canister.html | 2 +-
the_azle_book/book/cycles.html | 2 +-
the_azle_book/book/deployment.html | 2 +-
the_azle_book/book/examples.html | 6 +-
the_azle_book/book/http.html | 5 +-
the_azle_book/book/index.html | 2 +-
the_azle_book/book/installation.html | 12 +-
.../book/internet_computer_overview.html | 6 +-
the_azle_book/book/print.html | 1567 ++++++++---------
the_azle_book/book/searchindex.js | 2 +-
the_azle_book/book/searchindex.json | 2 +-
the_azle_book/book/timers.html | 2 +-
the_azle_book/src/SUMMARY.md | 4 +-
the_azle_book/src/azle.md | 28 +-
19 files changed, 1234 insertions(+), 1313 deletions(-)
diff --git a/README.md b/README.md
index 14e2ec9ae3..8566aa2236 100644
--- a/README.md
+++ b/README.md
@@ -15,25 +15,15 @@
# Azle (Beta)
-TypeScript CDK for the [Internet Computer](https://internetcomputer.org/).
+TypeScript and JavaScript CDK for the [Internet Computer](https://internetcomputer.org/).
## Disclaimer
Azle may have unknown security vulnerabilities due to the following:
-- Azle does not yet have many live, successful, continuously operating applications deployed to the IC
- Azle does not yet have extensive automated property tests
- Azle does not yet have multiple independent security reviews/audits
-- Azle heavily relies on Boa which is [self-proclaimed to be experimental](https://github.com/boa-dev/boa#boa)
-
-## Roadmap
-
-We hope to get to a production-ready 1.0 in 2024. The following are the major blockers to 1.0:
-
-- QuickJS/SpiderMonkey integration for performance, security, and stability
-- Broad npm package support
-- Extensive automated property testing
-- Multiple independent security reviews/audits
+- Azle does not yet have many live, successful, continuously operating applications deployed to the IC
## Documentation
@@ -43,6 +33,22 @@ See [The Azle Book](https://demergent-labs.github.io/azle/).
Feel free to open issues or join us in the [Discord channel](https://discord.gg/5Hb6rM2QUM).
+## Roadmap
+
+We hope to move Azle from beta to release candidates by the end of 2023, and to move from release candidates to 1.0 in early 2024.
+
+### Blockers for release candidates
+
+- Good automated property test coverage
+- Settling of API/syntax
+- Good npm package support
+
+### Blockers for 1.0
+
+- Extensive automated property test coverage
+- Multiple independent security reviews/audits
+- Broad npm package support
+
## Contributing
All contributors must agree to and sign the [Azle License Extension](/LICENSE_EXTENSION.md).
diff --git a/the_azle_book/book/404.html b/the_azle_book/book/404.html
index 069ea6598a..cc0bf4e3f6 100644
--- a/the_azle_book/book/404.html
+++ b/the_azle_book/book/404.html
@@ -84,7 +84,7 @@
diff --git a/the_azle_book/book/azle.html b/the_azle_book/book/azle.html
index bcf8a237c2..926c025b08 100644
--- a/the_azle_book/book/azle.html
+++ b/the_azle_book/book/azle.html
@@ -83,7 +83,7 @@
@@ -145,27 +145,32 @@
-Azle is a TypeScript Canister Development Kit (CDK) for the Internet Computer (IC). In other words, it's a TypeScript/JavaScript runtime for building applications (canisters ) on the IC.
+Azle is a TypeScript and JavaScript Canister Development Kit (CDK) for the Internet Computer (IC). In other words, it's a TypeScript/JavaScript runtime for building applications (canisters ) on the IC.
Azle may have unknown security vulnerabilities due to the following:
-Azle does not yet have many live, successful, continuously operating applications deployed to the IC
Azle does not yet have extensive automated property tests
Azle does not yet have multiple independent security reviews/audits
-Azle heavily relies on Boa which is self-proclaimed to be experimental
+Azle does not yet have many live, successful, continuously operating applications deployed to the IC
-We hope to get to a production-ready 1.0 in 2024. The following are the major blockers to 1.0:
+We hope to move Azle from beta to release candidates by the end of 2023, and to move from release candidates to 1.0 in early 2024.
+
-QuickJS/SpiderMonkey integration for performance, security, and stability
-Broad npm package support
-Extensive automated property testing
+Good automated property test coverage
+Settling of API/syntax
+Good npm package support
+
+
+
+Extensive automated property test coverage
Multiple independent security reviews/audits
+Broad npm package support
Azle is currently developed by Demergent Labs , a for-profit company with a grant from DFINITY .
@@ -174,8 +179,8 @@
-
+
@@ -323,7 +328,7 @@
-
+
diff --git a/the_azle_book/book/candid.html b/the_azle_book/book/candid.html
index ed39030410..dc7bb3818e 100644
--- a/the_azle_book/book/candid.html
+++ b/the_azle_book/book/candid.html
@@ -83,7 +83,7 @@
@@ -179,112 +179,112 @@
A reference of all Candid types available on the Internet Computer (IC) can be found here .
The following is a simple example showing how to import and use most of the Candid types available in Azle:
import {
+ Canister,
+ Func,
+ None,
+ Null,
+ Opt,
+ Principal,
+ Record,
+ Recursive,
+ Variant,
+ Vec,
blob,
- CallResult,
- float64,
+ bool,
float32,
- Func,
+ float64,
int,
- int8,
int16,
int32,
int64,
+ int8,
nat,
- nat8,
nat16,
nat32,
nat64,
- Opt,
- Principal,
- $query,
- Query,
- Record,
- Service,
- serviceQuery,
- serviceUpdate,
- Variant,
- Vec
+ nat8,
+ query,
+ text,
+ update
} from 'azle';
-type Candid = Record<{
- text: string;
- blob: blob;
- nat: nat;
- nat64: nat64;
- nat32: nat32;
- nat16: nat16;
- nat8: nat8;
- int: int;
- int64: int64;
- int32: int32;
- int16: int16;
- int8: int8;
- float64: float64;
- float32: float32;
- bool: boolean;
- null: null;
- vec: Vec<string>;
- opt: Opt<nat>;
- record: Record<{
- firstName: string;
- lastName: string;
- age: nat8;
- }>;
- variant: Variant<{
- Tag1: null;
- Tag2: null;
- Tag3: int;
- }>;
- func: Func<Query<() => Candid>>;
- service: MyService;
- principal: Principal;
-}>;
-
-class MyService extends Service {
- @serviceQuery
- query1: () => CallResult<boolean>;
-
- @serviceUpdate
- update1: () => CallResult<string>;
-}
-
-$query;
-export function candidTypes(): Candid {
- return {
- text: 'text',
- blob: Uint8Array.from([]),
- nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n,
- nat64: 18_446_744_073_709_551_615n,
- nat32: 4_294_967_295,
- nat16: 65_535,
- nat8: 255,
- int: 170_141_183_460_469_231_731_687_303_715_884_105_727n,
- int64: 9_223_372_036_854_775_807n,
- int32: 2_147_483_647,
- int16: 32_767,
- int8: 127,
- float64: Math.E,
- float32: Math.PI,
- bool: true,
- null: null,
- vec: ['has one element'],
- opt: Opt.None,
- record: {
- firstName: 'John',
- lastName: 'Doe',
- age: 35
- },
- variant: {
- Tag1: null
- },
- func: [
- Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
- 'candidTypes'
- ],
- service: new MyService(Principal.fromText('aaaaa-aa')),
- principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
- };
-}
+const MyCanister = Canister({
+ query1: query([], bool),
+ update1: update([], text)
+});
+
+const Candid = Record({
+ text: text,
+ blob: blob,
+ nat: nat,
+ nat64: nat64,
+ nat32: nat32,
+ nat16: nat16,
+ nat8: nat8,
+ int: int,
+ int64: int64,
+ int32: int32,
+ int16: int16,
+ int8: int8,
+ float64: float64,
+ float32: float32,
+ bool: bool,
+ null: Null,
+ vec: Vec(text),
+ opt: Opt(nat),
+ record: Record({
+ firstName: text,
+ lastName: text,
+ age: nat8
+ }),
+ variant: Variant({
+ Tag1: Null,
+ Tag2: Null,
+ Tag3: int
+ }),
+ func: Recursive(() => Func([], Candid, 'query')),
+ canister: MyCanister,
+ principal: Principal
+});
+
+export default Canister({
+ candidTypes: query([], Candid, () => {
+ return {
+ text: 'text',
+ blob: Uint8Array.from([]),
+ nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n,
+ nat64: 18_446_744_073_709_551_615n,
+ nat32: 4_294_967_295,
+ nat16: 65_535,
+ nat8: 255,
+ int: 170_141_183_460_469_231_731_687_303_715_884_105_727n,
+ int64: 9_223_372_036_854_775_807n,
+ int32: 2_147_483_647,
+ int16: 32_767,
+ int8: 127,
+ float64: Math.E,
+ float32: Math.PI,
+ bool: true,
+ null: null,
+ vec: ['has one element'],
+ opt: None,
+ record: {
+ firstName: 'John',
+ lastName: 'Doe',
+ age: 35
+ },
+ variant: {
+ Tag1: null
+ },
+ func: [
+ Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
+ 'candidTypes'
+ ],
+ canister: MyCanister(Principal.fromText('aaaaa-aa')),
+ principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
+ };
+ })
+});
Calling candidTypes
with dfx
will return:
(
@@ -318,18 +318,17 @@
The TypeScript type string
and the Azle type text
both correspond to the Candid type text and will become a JavaScript String at runtime.
TypeScript:
-import { $query } from 'azle';
-
-$query;
-export function getString(): string {
- return 'Hello world!';
-}
-
-$query;
-export function printString(string: string): string {
- console.log(typeof string);
- return string;
-}
+import { Canister, query, text } from 'azle';
+
+export default Canister({
+ getString: query([], text, () => {
+ return 'Hello world!';
+ }),
+ printString: query([text], text, (string) => {
+ console.log(typeof string);
+ return string;
+ })
+});
Candid:
service : () -> {
@@ -344,18 +343,17 @@
The Azle type blob
corresponds to the Candid type blob and will become a JavaScript Uint8Array at runtime.
TypeScript:
-import { blob, $query } from 'azle';
-
-$query;
-export function getBlob(): blob {
- return Uint8Array.from([68, 73, 68, 76, 0, 0]);
-}
-
-$query;
-export function printBlob(blob: blob): blob {
- console.log(typeof blob);
- return blob;
-}
+import { Canister, blob, query } from 'azle';
+
+export default Canister({
+ getBlob: query([], blob, () => {
+ return Uint8Array.from([68, 73, 68, 76, 0, 0]);
+ }),
+ printBlob: query([blob], blob, (blob) => {
+ console.log(typeof blob);
+ return blob;
+ })
+});
Candid:
service : () -> {
@@ -373,18 +371,17 @@
The Azle type nat
corresponds to the Candid type nat and will become a JavaScript BigInt at runtime.
TypeScript:
-import { nat, $query } from 'azle';
-
-$query;
-export function getNat(): nat {
- return 340_282_366_920_938_463_463_374_607_431_768_211_455n;
-}
-
-$query;
-export function printNat(nat: nat): nat {
- console.log(typeof nat);
- return nat;
-}
+import { Canister, nat, query } from 'azle';
+
+export default Canister({
+ getNat: query([], nat, () => {
+ return 340_282_366_920_938_463_463_374_607_431_768_211_455n;
+ }),
+ printNat: query([nat], nat, (nat) => {
+ console.log(typeof nat);
+ return nat;
+ })
+});
Candid:
service : () -> {
@@ -399,18 +396,17 @@
The Azle type nat64
corresponds to the Candid type nat64 and will become a JavaScript BigInt at runtime.
TypeScript:
-import { nat64, $query } from 'azle';
-
-$query;
-export function getNat64(): nat64 {
- return 18_446_744_073_709_551_615n;
-}
-
-$query;
-export function printNat64(nat64: nat64): nat64 {
- console.log(typeof nat64);
- return nat64;
-}
+import { Canister, nat64, query } from 'azle';
+
+export default Canister({
+ getNat64: query([], nat64, () => {
+ return 18_446_744_073_709_551_615n;
+ }),
+ printNat64: query([nat64], nat64, (nat64) => {
+ console.log(typeof nat64);
+ return nat64;
+ })
+});
Candid:
service : () -> {
@@ -425,18 +421,17 @@
The Azle type nat32
corresponds to the Candid type nat32 and will become a JavaScript Number at runtime.
TypeScript:
-import { nat32, $query } from 'azle';
-
-$query;
-export function getNat32(): nat32 {
- return 4_294_967_295;
-}
-
-$query;
-export function printNat32(nat32: nat32): nat32 {
- console.log(typeof nat32);
- return nat32;
-}
+import { Canister, nat32, query } from 'azle';
+
+export default Canister({
+ getNat32: query([], nat32, () => {
+ return 4_294_967_295;
+ }),
+ printNat32: query([nat32], nat32, (nat32) => {
+ console.log(typeof nat32);
+ return nat32;
+ })
+});
Candid:
service : () -> {
@@ -451,18 +446,17 @@
The Azle type nat16
corresponds to the Candid type nat16 and will become a JavaScript Number at runtime.
TypeScript:
-import { nat16, $query } from 'azle';
-
-$query;
-export function getNat16(): nat16 {
- return 65_535;
-}
-
-$query;
-export function printNat16(nat16: nat16): nat16 {
- console.log(typeof nat16);
- return nat16;
-}
+import { Canister, nat16, query } from 'azle';
+
+export default Canister({
+ getNat16: query([], nat16, () => {
+ return 65_535;
+ }),
+ printNat16: query([nat16], nat16, (nat16) => {
+ console.log(typeof nat16);
+ return nat16;
+ })
+});
Candid:
service : () -> {
@@ -477,18 +471,17 @@
The Azle type nat8
corresponds to the Candid type nat8 and will become a JavaScript Number at runtime.
TypeScript:
-import { nat8, $query } from 'azle';
-
-$query;
-export function getNat8(): nat8 {
- return 255;
-}
-
-$query;
-export function printNat8(nat8: nat8): nat8 {
- console.log(typeof nat8);
- return nat8;
-}
+import { Canister, nat8, query } from 'azle';
+
+export default Canister({
+ getNat8: query([], nat8, () => {
+ return 255;
+ }),
+ printNat8: query([nat8], nat8, (nat8) => {
+ console.log(typeof nat8);
+ return nat8;
+ })
+});
Candid:
service : () -> {
@@ -503,18 +496,17 @@
The Azle type int
corresponds to the Candid type int and will become a JavaScript BigInt at runtime.
TypeScript:
-import { int, $query } from 'azle';
-
-$query;
-export function getInt(): int {
- return 170_141_183_460_469_231_731_687_303_715_884_105_727n;
-}
-
-$query;
-export function printInt(int: int): int {
- console.log(typeof int);
- return int;
-}
+import { Canister, int, query } from 'azle';
+
+export default Canister({
+ getInt: query([], int, () => {
+ return 170_141_183_460_469_231_731_687_303_715_884_105_727n;
+ }),
+ printInt: query([int], int, (int) => {
+ console.log(typeof int);
+ return int;
+ })
+});
Candid:
service : () -> {
@@ -529,18 +521,17 @@
The Azle type int64
corresponds to the Candid type int64 and will become a JavaScript BigInt at runtime.
TypeScript:
-import { int64, $query } from 'azle';
-
-$query;
-export function getInt64(): int64 {
- return 9_223_372_036_854_775_807n;
-}
-
-$query;
-export function printInt64(int64: int64): int64 {
- console.log(typeof int64);
- return int64;
-}
+import { Canister, int64, query } from 'azle';
+
+export default Canister({
+ getInt64: query([], int64, () => {
+ return 9_223_372_036_854_775_807n;
+ }),
+ printInt64: query([int64], int64, (int64) => {
+ console.log(typeof int64);
+ return int64;
+ })
+});
Candid:
service : () -> {
@@ -555,18 +546,17 @@
The Azle type int32
corresponds to the Candid type int32 and will become a JavaScript Number at runtime.
TypeScript:
-import { int32, $query } from 'azle';
-
-$query;
-export function getInt32(): int32 {
- return 2_147_483_647;
-}
-
-$query;
-export function printInt32(int32: int32): int32 {
- console.log(typeof int32);
- return int32;
-}
+import { Canister, int32, query } from 'azle';
+
+export default Canister({
+ getInt32: query([], int32, () => {
+ return 2_147_483_647;
+ }),
+ printInt32: query([int32], int32, (int32) => {
+ console.log(typeof int32);
+ return int32;
+ })
+});
Candid:
service : () -> {
@@ -581,18 +571,17 @@
The Azle type int16
corresponds to the Candid type int16 and will become a JavaScript Number at runtime.
TypeScript:
-import { int16, $query } from 'azle';
-
-$query;
-export function getInt16(): int16 {
- return 32_767;
-}
-
-$query;
-export function printInt16(int16: int16): int16 {
- console.log(typeof int16);
- return int16;
-}
+import { Canister, int16, query } from 'azle';
+
+export default Canister({
+ getInt16: query([], int16, () => {
+ return 32_767;
+ }),
+ printInt16: query([int16], int16, (int16) => {
+ console.log(typeof int16);
+ return int16;
+ })
+});
Candid:
service : () -> {
@@ -607,18 +596,17 @@
The Azle type int8
corresponds to the Candid type int8 and will become a JavaScript Number at runtime.
TypeScript:
-import { int8, $query } from 'azle';
-
-$query;
-export function getInt8(): int8 {
- return 127;
-}
-
-$query;
-export function printInt8(int8: int8): int8 {
- console.log(typeof int8);
- return int8;
-}
+import { Canister, int8, query } from 'azle';
+
+export default Canister({
+ getInt8: query([], int8, () => {
+ return 127;
+ }),
+ printInt8: query([int8], int8, (int8) => {
+ console.log(typeof int8);
+ return int8;
+ })
+});
Candid:
service : () -> {
@@ -633,18 +621,17 @@
The Azle type float64
and the TypeScript type number
both correspond to the Candid type float64 and will become a JavaScript Number at runtime.
TypeScript:
-import { float64, $query } from 'azle';
-
-$query;
-export function getFloat64(): float64 {
- return Math.E;
-}
-
-$query;
-export function printFloat64(float64: float64): float64 {
- console.log(typeof float64);
- return float64;
-}
+import { Canister, float64, query } from 'azle';
+
+export default Canister({
+ getFloat64: query([], float64, () => {
+ return Math.E;
+ }),
+ printFloat64: query([float64], float64, (float64) => {
+ console.log(typeof float64);
+ return float64;
+ })
+});
Candid:
service : () -> {
@@ -659,18 +646,17 @@
The Azle type float32
corresponds to the Candid type float32 and will become a JavaScript Number at runtime.
TypeScript:
-import { float32, $query } from 'azle';
-
-$query;
-export function getFloat32(): float32 {
- return Math.PI;
-}
-
-$query;
-export function printFloat32(float32: float32): float32 {
- console.log(typeof float32);
- return float32;
-}
+import { Canister, float32, query } from 'azle';
+
+export default Canister({
+ getFloat32: query([], float32, () => {
+ return Math.PI;
+ }),
+ printFloat32: query([float32], float32, (float32) => {
+ console.log(typeof float32);
+ return float32;
+ })
+});
Candid:
service : () -> {
@@ -685,18 +671,17 @@
The TypeScript type boolean
corresponds to the Candid type bool and will become a JavaScript Boolean at runtime.
TypeScript:
-import { $query } from 'azle';
-
-$query;
-export function getBool(): boolean {
- return true;
-}
-
-$query;
-export function printBool(bool: boolean): boolean {
- console.log(typeof bool);
- return bool;
-}
+import { Canister, bool, query } from 'azle';
+
+export default Canister({
+ getBool: query([], bool, () => {
+ return true;
+ }),
+ printBool: query([bool], bool, (bool) => {
+ console.log(typeof bool);
+ return bool;
+ })
+});
Candid:
service : () -> {
@@ -711,18 +696,17 @@
The TypeScript type null
corresponds to the Candid type null and will become a JavaScript null at runtime.
TypeScript:
-import { $query } from 'azle';
-
-$query;
-export function getNull(): null {
- return null;
-}
-
-$query;
-export function printNull(null_: null): null {
- console.log(typeof null_);
- return null_;
-}
+import { Canister, Null, query } from 'azle';
+
+export default Canister({
+ getNull: query([], Null, () => {
+ return null;
+ }),
+ printNull: query([Null], Null, (null_) => {
+ console.log(typeof null_);
+ return null_;
+ })
+});
Candid:
service : () -> {
@@ -737,18 +721,17 @@
The Azle type Vec
corresponds to the Candid type vec and will become a JavaScript array of the specified type at runtime (except for Vec<nat8>
which will become a Uint8Array
, thus it is recommended to use the blob
type instead of Vec<nat8>
).
TypeScript:
-import { int32, $query, Vec } from 'azle';
-
-$query;
-export function getNumbers(): Vec<int32> {
- return [0, 1, 2, 3];
-}
-
-$query;
-export function printNumbers(numbers: Vec<int32>): Vec<int32> {
- console.log(typeof numbers);
- return numbers;
-}
+import { Canister, Vec, int32, query } from 'azle';
+
+export default Canister({
+ getNumbers: query([], Vec(int32), () => {
+ return [0, 1, 2, 3];
+ }),
+ printNumbers: query([Vec(int32)], Vec(int32), (numbers) => {
+ console.log(typeof numbers);
+ return numbers;
+ })
+});
Candid:
service : () -> {
@@ -763,17 +746,16 @@
The Azle type Opt
corresponds to the Candid type opt . It is a variant with Some
and None
cases. At runtime if the value of the variant is Some
, the Some
property of the variant object will have a value of the enclosed Opt
type at runtime.
TypeScript:
-import { Opt, $query } from 'azle';
-
-$query;
-export function getOptSome(): Opt<boolean> {
- return Opt.Some(true);
-}
+import { Canister, None, Opt, Some, bool, query } from 'azle';
-$query;
-export function getOptNone(): Opt<boolean> {
- return Opt.None;
-}
+export default Canister({
+ getOptSome: query([], Opt(bool), () => {
+ return Some(true);
+ }),
+ getOptNone: query([], Opt(bool), () => {
+ return None;
+ })
+});
Candid:
service : () -> {
@@ -791,26 +773,25 @@
TypeScript type aliases referring to object literals wrapped in the Record
Azle type correspond to the Candid record type and will become JavaScript Objects at runtime.
TypeScript:
-import { Principal, $query, Record } from 'azle';
-
-type User = Record<{
- id: Principal;
- username: string;
-}>;
-
-$query;
-export function getUser(): User {
- return {
- id: Principal.fromUint8Array(Uint8Array.from([0])),
- username: 'lastmjs'
- };
-}
-
-$query;
-export function printUser(user: User): User {
- console.log(typeof user);
- return user;
-}
+import { Canister, Principal, Record, query, text } from 'azle';
+
+const User = Record({
+ id: Principal,
+ username: text
+});
+
+export default Canister({
+ getUser: query([], User, () => {
+ return {
+ id: Principal.fromUint8Array(Uint8Array.from([0])),
+ username: 'lastmjs'
+ };
+ }),
+ printUser: query([User], User, (user) => {
+ console.log(typeof user);
+ return user;
+ })
+});
Candid:
type User = record { id : principal; username : text };
@@ -826,32 +807,31 @@
TypeScript type aliases referring to object literals wrapped in the Variant
Azle type correspond to the Candid variant type and will become JavaScript Objects at runtime.
TypeScript:
-import { $query, Variant } from 'azle';
-
-type Reaction = Variant<{
- Fire: null;
- ThumbsUp: null;
- Emotion: Emotion;
-}>;
-
-type Emotion = Variant<{
- Happy: null;
- Indifferent: null;
- Sad: null;
-}>;
-
-$query;
-export function getReaction(): Reaction {
- return {
- Fire: null
- };
-}
-
-$query;
-export function printReaction(reaction: Reaction): Reaction {
- console.log(typeof reaction);
- return reaction;
-}
+import { Canister, Null, Variant, query } from 'azle';
+
+const Emotion = Variant({
+ Happy: Null,
+ Indifferent: Null,
+ Sad: Null
+});
+
+const Reaction = Variant({
+ Fire: Null,
+ ThumbsUp: Null,
+ Emotion: Emotion
+});
+
+export default Canister({
+ getReaction: query([], Reaction, () => {
+ return {
+ Fire: null
+ };
+ }),
+ printReaction: query([Reaction], Reaction, (reaction) => {
+ console.log(typeof reaction);
+ return reaction;
+ })
+});
Candid:
type Emotion = variant { Sad; Indifferent; Happy };
@@ -870,20 +850,22 @@
The first element is an @dfinity/principal and the second is a JavaScript string . The @dfinity/principal
represents the principal
of the canister/service where the function exists, and the string
represents the function's name.
A func
acts as a callback, allowing the func
receiver to know which canister instance and method must be used to call back.
TypeScript:
-import { Func, Principal, $query, Query } from 'azle';
+import { Canister, Func, Principal, query, text } from 'azle';
-type BasicFunc = Func<Query<(param1: string) => string>>;
+const BasicFunc = Func([text], text, 'query');
-$query;
-export function getBasicFunc(): BasicFunc {
- return [Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), 'getBasicFunc'];
-}
-
-$query;
-export function printBasicFunc(basicFunc: BasicFunc): BasicFunc {
- console.log(typeof basicFunc);
- return basicFunc;
-}
+export default Canister({
+ getBasicFunc: query([], BasicFunc, () => {
+ return [
+ Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
+ 'getBasicFunc'
+ ];
+ }),
+ printBasicFunc: query([BasicFunc], BasicFunc, (basicFunc) => {
+ console.log(typeof basicFunc);
+ return basicFunc;
+ })
+});
Candid:
service : () -> {
@@ -900,36 +882,21 @@
JavaScript classes that inherit from the Azle type Service
correspond to the Candid service type and will become child classes capable of creating instances that can perform cross-canister calls at runtime.
TypeScript:
-import {
- CallResult,
- Principal,
- $query,
- Result,
- Service,
- serviceQuery,
- serviceUpdate,
- $update
-} from 'azle';
-
-class SomeService extends Service {
- @serviceQuery
- query1: () => CallResult<boolean>;
-
- @serviceUpdate
- update1: () => CallResult<string>;
-}
-
-$query;
-export function getService(): SomeService {
- return new SomeService(Principal.fromText('aaaaa-aa'));
-}
-
-$update;
-export async function callService(
- service: SomeService
-): Promise<Result<string, string>> {
- return await service.update1().call();
-}
+import { Canister, Principal, bool, ic, query, text, update } from 'azle';
+
+const SomeCanister = Canister({
+ query1: query([], bool),
+ update1: update([], text)
+});
+
+export default Canister({
+ getService: query([], SomeCanister, () => {
+ return SomeCanister(Principal.fromText('aaaaa-aa'));
+ }),
+ callService: update([SomeCanister], text, (service) => {
+ return ic.call(service.update1);
+ })
+});
Candid:
type ManualReply = variant { Ok : text; Err : text };
@@ -949,18 +916,17 @@
The Azle type Principal
corresponds to the Candid type principal and will become an @dfinity/principal at runtime.
TypeScript:
-import { Principal, $query } from 'azle';
-
-$query;
-export function getPrincipal(): Principal {
- return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai');
-}
-
-$query;
-export function printPrincipal(principal: Principal): Principal {
- console.log(typeof principal);
- return principal;
-}
+import { Canister, Principal, query } from 'azle';
+
+export default Canister({
+ getPrincipal: query([], Principal, () => {
+ return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai');
+ }),
+ printPrincipal: query([Principal], Principal, (principal) => {
+ console.log(typeof principal);
+ return principal;
+ })
+});
Candid:
service : () -> {
@@ -975,18 +941,17 @@
The Azle type reserved
corresponds to the Candid type reserved , is the TypeScript type any
, and will become a JavaScript null at runtime.
TypeScript:
-import { $query, reserved } from 'azle';
-
-$query;
-export function getReserved(): reserved {
- return 'anything';
-}
-
-$query;
-export function printReserved(reserved: reserved): reserved {
- console.log(typeof reserved);
- return reserved;
-}
+import { Canister, query, reserved } from 'azle';
+
+export default Canister({
+ getReserved: query([], reserved, () => {
+ return 'anything';
+ }),
+ printReserved: query([reserved], reserved, (reserved) => {
+ console.log(typeof reserved);
+ return reserved;
+ })
+});
Candid:
service : () -> {
@@ -1001,20 +966,19 @@
The Azle type empty
corresponds to the Candid type empty and has no JavaScript value at runtime.
TypeScript:
-import { empty, $query } from 'azle';
-
-$query;
-export function getEmpty(): empty {
- throw 'Anything you want';
-}
-
-// Note: It is impossible to call this function because it requires an argument
-// but there is no way to pass an "empty" value as an argument.
-$query;
-export function printEmpty(empty: empty): empty {
- console.log(typeof empty);
- throw 'Anything you want';
-}
+import { Canister, empty, query } from 'azle';
+
+export default Canister({
+ getEmpty: query([], empty, () => {
+ throw 'Anything you want';
+ }),
+ // Note: It is impossible to call this function because it requires an argument
+ // but there is no way to pass an "empty" value as an argument.
+ printEmpty: query([empty], empty, (empty) => {
+ console.log(typeof empty);
+ throw 'Anything you want';
+ })
+});
Candid:
service : () -> {
diff --git a/the_azle_book/book/canisters_overview.html b/the_azle_book/book/canisters_overview.html
index 22c7d7b1bc..77775a7a7f 100644
--- a/the_azle_book/book/canisters_overview.html
+++ b/the_azle_book/book/canisters_overview.html
@@ -83,7 +83,7 @@
@@ -158,7 +158,7 @@
-
+
@@ -172,7 +172,7 @@
-
+
diff --git a/the_azle_book/book/cross_canister.html b/the_azle_book/book/cross_canister.html
index d3d542bea0..d010317e50 100644
--- a/the_azle_book/book/cross_canister.html
+++ b/the_azle_book/book/cross_canister.html
@@ -83,7 +83,7 @@
diff --git a/the_azle_book/book/cycles.html b/the_azle_book/book/cycles.html
index 30109de2ee..f865726fab 100644
--- a/the_azle_book/book/cycles.html
+++ b/the_azle_book/book/cycles.html
@@ -83,7 +83,7 @@
diff --git a/the_azle_book/book/deployment.html b/the_azle_book/book/deployment.html
index 4eaf38ce80..ce157b492c 100644
--- a/the_azle_book/book/deployment.html
+++ b/the_azle_book/book/deployment.html
@@ -83,7 +83,7 @@
diff --git a/the_azle_book/book/examples.html b/the_azle_book/book/examples.html
index 99d4e25f8c..b058129049 100644
--- a/the_azle_book/book/examples.html
+++ b/the_azle_book/book/examples.html
@@ -83,7 +83,7 @@
@@ -174,7 +174,7 @@
-
+
@@ -188,7 +188,7 @@
-
+
diff --git a/the_azle_book/book/http.html b/the_azle_book/book/http.html
index 77a440f3e5..d07d3c206d 100644
--- a/the_azle_book/book/http.html
+++ b/the_azle_book/book/http.html
@@ -83,7 +83,7 @@
@@ -219,7 +219,6 @@ outgoing_http_requests
import {
- Alias,
ic,
$init,
match,
@@ -237,7 +236,7 @@
-
+
diff --git a/the_azle_book/book/internet_computer_overview.html b/the_azle_book/book/internet_computer_overview.html
index dd04ce9018..98b23331a4 100644
--- a/the_azle_book/book/internet_computer_overview.html
+++ b/the_azle_book/book/internet_computer_overview.html
@@ -83,7 +83,7 @@
@@ -157,7 +157,7 @@
+
@@ -171,7 +171,7 @@
+
diff --git a/the_azle_book/book/print.html b/the_azle_book/book/print.html
index d3fd572f36..11c2f80163 100644
--- a/the_azle_book/book/print.html
+++ b/the_azle_book/book/print.html
@@ -84,7 +84,7 @@
@@ -179,27 +179,32 @@
SOFTWARE.
-Azle is a TypeScript Canister Development Kit (CDK) for the Internet Computer (IC). In other words, it's a TypeScript/JavaScript runtime for building applications (canisters ) on the IC.
+Azle is a TypeScript and JavaScript Canister Development Kit (CDK) for the Internet Computer (IC). In other words, it's a TypeScript/JavaScript runtime for building applications (canisters ) on the IC.
Azle may have unknown security vulnerabilities due to the following:
-Azle does not yet have many live, successful, continuously operating applications deployed to the IC
Azle does not yet have extensive automated property tests
Azle does not yet have multiple independent security reviews/audits
-Azle heavily relies on Boa which is self-proclaimed to be experimental
+Azle does not yet have many live, successful, continuously operating applications deployed to the IC
-We hope to get to a production-ready 1.0 in 2024. The following are the major blockers to 1.0:
+We hope to move Azle from beta to release candidates by the end of 2023, and to move from release candidates to 1.0 in early 2024.
+
-QuickJS/SpiderMonkey integration for performance, security, and stability
-Broad npm package support
-Extensive automated property testing
+Good automated property test coverage
+Settling of API/syntax
+Good npm package support
+
+
+
+Extensive automated property test coverage
Multiple independent security reviews/audits
+Broad npm package support
Azle is currently developed by Demergent Labs , a for-profit company with a grant from DFINITY .
@@ -208,8 +213,8 @@
The following information will help you to determine when Azle and the IC might be beneficial for your use-case.
-Azle intends to be a full TypeScript/JavaScript environment for the IC (a decentralized cloud platform), with support for all of the TypeScript/JavaScript language and as many relevant host APIs as possible. These host APIs will be similar to those available in the Node.js and web browser environments.
-One of the core benefits of Azle is that it allows web developers to bring their TypeScript/JavaScript skills to the IC. For example, Azle allows the use of various npm packages and VS Code intellisense.
+Azle intends to be a full TypeScript and JavaScript environment for the IC (a decentralized cloud platform), with support for all of the TypeScript and JavaScript language and as many relevant environment APIs as possible. These environment APIs will be similar to those available in the Node.js and web browser environments.
+One of the core benefits of Azle is that it allows web developers to bring their TypeScript or JavaScript skills to the IC. For example, Azle allows the use of various npm packages and VS Code intellisense.
As for the IC, we believe its main benefits can be broken down into the following categories:
Ownership
@@ -229,7 +234,7 @@ . As opposed to DAOs built on most blockchains, the IC allows full-stack applications to be controlled by groups. This means that the group fully controls the running instances of the frontend and the backend code.
- In addition to allowing applications to be owned by groups of people, the IC also allows applications to be owned by no one. This essentially creates autonomous applications or everlasting processes that execute indefinitely. The IC will allow such an application to run until it depletes its balance of cycles, or until the NNS votes to shut it down.
+In addition to allowing applications to be owned by groups of people, the IC also allows applications to be owned by no one. This essentially creates autonomous applications or everlasting processes that execute indefinitely. The IC will essentially allow such an application to run indefinitely, unless it depletes its balance of cycles, or the NNS votes to shut it down, neither of which is inevitable.
Because most web APIs are owned and operated by individual entities, their fate is tied to that of their owners. If their owners go out of business, then those APIs may cease to exist. If their owners decide that they do not like or agree with certain users, they may restrict their access. In the end, they may decide to shut down or restrict access for arbitrary reasons.
Because the IC allows for group and autonomous ownership of cloud software, the IC is able to produce potentially permanent web APIs. A decentralized group of independent entities will find it difficult to censor API consumers or shut down an API. An autonomous API would take those difficulties to the extreme, as it would continue operating as long as consumers were willing to pay for it.
@@ -334,12 +339,25 @@
-
-The Internet Computer (IC) is a decentralized cloud platform. Actually, it is better thought of as a progressively decentralizing cloud platform. Its full vision is yet to be fulfilled.
-It aims to be owned and operated by many independent entities in many geographies and legal jurisdictions throughout the world. This is in opposition to most traditional cloud platforms today, which are generally owned and operated by one overarching legal entity.
-The IC is composed of computer hardware nodes running the IC protocol software. Each running IC protocol software process is known as a replica.
-Nodes are assigned into groups known as subnets. Each subnet attempts to maximize its decentralization of nodes according to factors such as data center location and node operator independence.
-The subnets vary in size. Generally speaking the larger the size of the subnet the more secure it will be. Subnets currently range in size from 13 to 40 nodes, with most subnets having 13 nodes.
-IC applications, known as canisters, are deployed to specific subnets. They are then accessible through Internet Protocol requests such as HTTP. Each subnet replicates all canisters across all of its replicas. A consensus protocol is run by the replicas to ensure Byzantine Fault Tolerance .
-View the IC Dashboard to explore all data centers, subnets, node operators, and many other aspects of the IC.
-
-Canisters are Internet Computer (IC) applications. They are the encapsulation of your code and state, and are essentially Wasm modules.
-State can be stored on the 4 GiB heap or in a larger 64 GiB location called stable memory. You can store state on the heap using your language's native global variables. You can store state in stable memory using low-level APIs or special stable data structures that behave similarly to native language data structures.
-State changes must go through a process called consensus. The consensus process ensures that state changes are Byzantine Fault Tolerant . This process takes a few seconds to complete.
-Operations on canister state are exposed to users through canister methods. These methods can be invoked through HTTP requests. Query methods allow state to be read and are low-latency. Update methods allow state to be changed and are higher-latency. Update methods take a few seconds to complete because of the consensus process.
-Annotate functions with $query
+Annotate functions with query
Read-only
Executed on a single node
No consensus
@@ -643,41 +644,44 @@
~32k queries per second per canister
The most basic way to expose your canister's functionality publicly is through a query method. Here's an example of a simple query method:
-import { $query } from 'azle';
+import { Canister, query, text } from 'azle';
-$query;
-export function getString(): string {
- return 'This is a query method!';
-}
+export default Canister({
+ getString: query([], text, () => {
+ return 'This is a query method!';
+ })
+});
getString
can be called from the outside world through the IC's HTTP API. You'll usually invoke this API from the dfx command line
, dfx web UI
, or an agent .
From the dfx command line
you can call it like this:
dfx canister call my_canister getString
Query methods are read-only. They do not persist any state changes. Take a look at the following example:
-import { $query } from 'azle';
+import { Canister, Void, query, text } from 'azle';
let db: {
[key: string]: string;
} = {};
-$query;
-export function set(key: string, value: string): void {
- db[key] = value;
-}
+export default Canister({
+ set: query([text, text], Void, (key, value) => {
+ db[key] = value;
+ })
+});
Calling set
will perform the operation of setting the key
property on the db
object to value
, but after the call finishes that change will be discarded.
This is because query methods are executed on a single node machine and do not go through consensus . This results in lower latencies, perhaps on the order of 100 milliseconds.
There is a limit to how much computation can be done in a single call to a query method. The current query call limit is 5 billion Wasm instructions . Here's an example of a query method that runs the risk of reaching the limit:
-import { nat32, $query } from 'azle';
+import { Canister, nat32, query, text } from 'azle';
-$query;
-export function pyramid(levels: nat32): string {
- return new Array(levels).fill(0).reduce((acc, _, index) => {
- const asterisks = new Array(index + 1).fill('*').join('');
- return `${acc}${asterisks}\n`;
- }, '');
-}
+export default Canister({
+ pyramid: query([nat32], text, (levels) => {
+ return new Array(levels).fill(0).reduce((acc, _, index) => {
+ const asterisks = new Array(index + 1).fill('*').join('');
+ return `${acc}${asterisks}\n`;
+ }, '');
+ })
+});
From the dfx command line
you can call pyramid
like this:
dfx canister call my_canister pyramid '(600)'
@@ -688,7 +692,7 @@
-Annotate functions with $update
+Annotate functions with update
Read-write
Executed on many nodes
Consensus
@@ -699,35 +703,35 @@
~900 updates per second per canister
Update methods are similar to query methods, but state changes can be persisted. Here's an example of a simple update method:
-import { nat64, $update } from 'azle';
+import { Canister, nat64, update } from 'azle';
let counter = 0n;
-$update;
-export function increment(): nat64 {
- return counter++;
-}
+export default Canister({
+ increment: update([], nat64, () => {
+ return counter++;
+ })
+});
Calling increment
will return the current value of counter
and then increase its value by 1. Because counter
is a global variable, the change will be persisted to the heap, and subsequent query and update calls will have access to the new counter
value.
Because the Internet Computer (IC) persists changes with certain fault tolerance guarantees, update calls are executed on many nodes and go through consensus . This leads to latencies of ~2-5 seconds per update call.
Due to the latency and other expenses involved with update methods, it is best to use them only when necessary. Look at the following example:
-import { $query, $update } from 'azle';
+import { Canister, Void, query, text, update } from 'azle';
let message = '';
-$query;
-export function getMessage(): string {
- return message;
-}
-
-$update;
-export function setMessage(newMessage: string): void {
- message = newMessage;
-}
+export default Canister({
+ getMessage: query([], text, () => {
+ return message;
+ }),
+ setMessage: update([text], Void, (newMessage) => {
+ message = newMessage;
+ })
+});
You'll notice that we use an update method, setMessage
, only to perform the change to the global message
variable. We use getMessage
, a query method, to read the message.
Keep in mind that the heap is limited to 4 GiB, and thus there is an upper bound to global variable storage capacity. You can imagine how a simple database like the following would eventually run out of memory with too many entries:
-import { Opt, $query, $update } from 'azle';
+import { Canister, None, Opt, Some, Void, query, text, update } from 'azle';
type Db = {
[key: string]: string;
@@ -735,87 +739,99 @@
let db: Db = {};
-$query;
-export function get(key: string): Opt<string> {
- const value = db[key];
- return value !== undefined ? Opt.Some(value) : Opt.None;
-}
-
-$update;
-export function set(key: string, value: string): void {
- db[key] = value;
-}
+export default Canister({
+ get: query([text], Opt(text), (key) => {
+ const value = db[key];
+ return value !== undefined ? Some(value) : None;
+ }),
+ set: update([text, text], Void, (key, value) => {
+ db[key] = value;
+ })
+});
If you need more than 4 GiB of storage, consider taking advantage of the 48 GiB of stable memory. Stable structures like StableBTreeMap
give you a nice API for interacting with stable memory. These data structures will be covered in more detail later . Here's a simple example:
-import { Opt, $query, StableBTreeMap, $update } from 'azle';
+import { Canister, Opt, StableBTreeMap, Void, query, text, update } from 'azle';
-let db = new StableBTreeMap<string, string>(0, 10, 10);
+let db = StableBTreeMap(text, text, 0);
-$query;
-export function get(key: string): Opt<string> {
- return db.get(key);
-}
-
-$update;
-export function set(key: string, value: string): void {
- db.insert(key, value);
-}
+export default Canister({
+ get: query([text], Opt(text), (key) => {
+ return db.get(key);
+ }),
+ set: update([text, text], Void, (key, value) => {
+ db.insert(key, value);
+ })
+});
So far we have only seen how state changes can be persisted. State changes can also be discarded by implicit or explicit traps. A trap is an immediate stop to execution with the ability to provide a message to the execution environment.
Traps can be useful for ensuring that multiple operations are either all completed or all disregarded, or in other words atomic. Keep in mind that these guarantees do not hold once cross-canister calls are introduced, but that's a more advanced topic covered later .
Here's an example of how to trap and ensure atomic changes to your database:
-import { ic, Opt, $query, Record, StableBTreeMap, $update, Vec } from 'azle';
-
-type Entry = Record<{
- key: string;
- value: string;
-}>;
-
-let db = new StableBTreeMap<string, string>(0, 10, 10);
-
-$query;
-export function get(key: string): Opt<string> {
- return db.get(key);
-}
-
-$update;
-export function set(key: string, value: string): void {
- db.insert(key, value);
-}
+import {
+ Canister,
+ Opt,
+ Record,
+ StableBTreeMap,
+ Vec,
+ Void,
+ ic,
+ query,
+ text,
+ update
+} from 'azle';
-$update;
-export function setMany(entries: Vec<Entry>): void {
- entries.forEach((entry) => {
- if (entry.key === 'trap') {
- ic.trap('explicit trap');
- }
+const Entry = Record({
+ key: text,
+ value: text
+});
+
+let db = StableBTreeMap(text, text, 0);
+
+export default Canister({
+ get: query([text], Opt(text), (key) => {
+ return db.get(key);
+ }),
+ set: update([text, text], Void, (key, value) => {
+ db.insert(key, value);
+ }),
+ setMany: update([Vec(Entry)], Void, (entries) => {
+ entries.forEach((entry) => {
+ if (entry.key === 'trap') {
+ ic.trap('explicit trap');
+ }
- db.insert(entry.key, entry.value);
- });
-}
+ db.insert(entry.key, entry.value);
+ });
+ })
+});
In addition to ic.trap
, an explicit JavaScript throw
or any unhandled exception will also trap.
There is a limit to how much computation can be done in a single call to an update method. The current update call limit is 20 billion Wasm instructions . If we modify our database example, we can introduce an update method that runs the risk of reaching the limit:
-import { nat64, Opt, $query, StableBTreeMap, $update } from 'azle';
-
-let db = new StableBTreeMap<string, string>(0, 1_000, 1_000);
-
-$query;
-export function get(key: string): Opt<string> {
- return db.get(key);
-}
-
-$update;
-export function set(key: string, value: string): void {
- db.insert(key, value);
-}
+import {
+ Canister,
+ Opt,
+ StableBTreeMap,
+ Void,
+ nat64,
+ query,
+ text,
+ update
+} from 'azle';
-$update;
-export function setMany(numEntries: nat64): void {
- for (let i = 0; i < numEntries; i++) {
- db.insert(i.toString(), i.toString());
- }
-}
+let db = StableBTreeMap(text, text, 0);
+
+export default Canister({
+ get: query([text], Opt(text), (key) => {
+ return db.get(key);
+ }),
+ set: update([text, text], Void, (key, value) => {
+ db.insert(key, value);
+ }),
+ setMany: update([nat64], Void, (numEntries) => {
+ for (let i = 0; i < numEntries; i++) {
+ db.insert(i.toString(), i.toString());
+ }
+ })
+});
From the dfx command line
you can call setMany
like this:
dfx canister call my_canister setMany '(100_000)'
@@ -857,112 +873,112 @@
A reference of all Candid types available on the Internet Computer (IC) can be found here .
The following is a simple example showing how to import and use most of the Candid types available in Azle:
import {
+ Canister,
+ Func,
+ None,
+ Null,
+ Opt,
+ Principal,
+ Record,
+ Recursive,
+ Variant,
+ Vec,
blob,
- CallResult,
- float64,
+ bool,
float32,
- Func,
+ float64,
int,
- int8,
int16,
int32,
int64,
+ int8,
nat,
- nat8,
nat16,
nat32,
nat64,
- Opt,
- Principal,
- $query,
- Query,
- Record,
- Service,
- serviceQuery,
- serviceUpdate,
- Variant,
- Vec
+ nat8,
+ query,
+ text,
+ update
} from 'azle';
-type Candid = Record<{
- text: string;
- blob: blob;
- nat: nat;
- nat64: nat64;
- nat32: nat32;
- nat16: nat16;
- nat8: nat8;
- int: int;
- int64: int64;
- int32: int32;
- int16: int16;
- int8: int8;
- float64: float64;
- float32: float32;
- bool: boolean;
- null: null;
- vec: Vec<string>;
- opt: Opt<nat>;
- record: Record<{
- firstName: string;
- lastName: string;
- age: nat8;
- }>;
- variant: Variant<{
- Tag1: null;
- Tag2: null;
- Tag3: int;
- }>;
- func: Func<Query<() => Candid>>;
- service: MyService;
- principal: Principal;
-}>;
-
-class MyService extends Service {
- @serviceQuery
- query1: () => CallResult<boolean>;
-
- @serviceUpdate
- update1: () => CallResult<string>;
-}
-
-$query;
-export function candidTypes(): Candid {
- return {
- text: 'text',
- blob: Uint8Array.from([]),
- nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n,
- nat64: 18_446_744_073_709_551_615n,
- nat32: 4_294_967_295,
- nat16: 65_535,
- nat8: 255,
- int: 170_141_183_460_469_231_731_687_303_715_884_105_727n,
- int64: 9_223_372_036_854_775_807n,
- int32: 2_147_483_647,
- int16: 32_767,
- int8: 127,
- float64: Math.E,
- float32: Math.PI,
- bool: true,
- null: null,
- vec: ['has one element'],
- opt: Opt.None,
- record: {
- firstName: 'John',
- lastName: 'Doe',
- age: 35
- },
- variant: {
- Tag1: null
- },
- func: [
- Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
- 'candidTypes'
- ],
- service: new MyService(Principal.fromText('aaaaa-aa')),
- principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
- };
-}
+const MyCanister = Canister({
+ query1: query([], bool),
+ update1: update([], text)
+});
+
+const Candid = Record({
+ text: text,
+ blob: blob,
+ nat: nat,
+ nat64: nat64,
+ nat32: nat32,
+ nat16: nat16,
+ nat8: nat8,
+ int: int,
+ int64: int64,
+ int32: int32,
+ int16: int16,
+ int8: int8,
+ float64: float64,
+ float32: float32,
+ bool: bool,
+ null: Null,
+ vec: Vec(text),
+ opt: Opt(nat),
+ record: Record({
+ firstName: text,
+ lastName: text,
+ age: nat8
+ }),
+ variant: Variant({
+ Tag1: Null,
+ Tag2: Null,
+ Tag3: int
+ }),
+ func: Recursive(() => Func([], Candid, 'query')),
+ canister: MyCanister,
+ principal: Principal
+});
+
+export default Canister({
+ candidTypes: query([], Candid, () => {
+ return {
+ text: 'text',
+ blob: Uint8Array.from([]),
+ nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n,
+ nat64: 18_446_744_073_709_551_615n,
+ nat32: 4_294_967_295,
+ nat16: 65_535,
+ nat8: 255,
+ int: 170_141_183_460_469_231_731_687_303_715_884_105_727n,
+ int64: 9_223_372_036_854_775_807n,
+ int32: 2_147_483_647,
+ int16: 32_767,
+ int8: 127,
+ float64: Math.E,
+ float32: Math.PI,
+ bool: true,
+ null: null,
+ vec: ['has one element'],
+ opt: None,
+ record: {
+ firstName: 'John',
+ lastName: 'Doe',
+ age: 35
+ },
+ variant: {
+ Tag1: null
+ },
+ func: [
+ Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
+ 'candidTypes'
+ ],
+ canister: MyCanister(Principal.fromText('aaaaa-aa')),
+ principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
+ };
+ })
+});
Calling candidTypes
with dfx
will return:
(
@@ -996,18 +1012,17 @@
The TypeScript type string
and the Azle type text
both correspond to the Candid type text and will become a JavaScript String at runtime.
TypeScript:
-import { $query } from 'azle';
-
-$query;
-export function getString(): string {
- return 'Hello world!';
-}
-
-$query;
-export function printString(string: string): string {
- console.log(typeof string);
- return string;
-}
+import { Canister, query, text } from 'azle';
+
+export default Canister({
+ getString: query([], text, () => {
+ return 'Hello world!';
+ }),
+ printString: query([text], text, (string) => {
+ console.log(typeof string);
+ return string;
+ })
+});
Candid:
service : () -> {
@@ -1022,18 +1037,17 @@
The Azle type blob
corresponds to the Candid type blob and will become a JavaScript Uint8Array at runtime.
TypeScript:
-import { blob, $query } from 'azle';
-
-$query;
-export function getBlob(): blob {
- return Uint8Array.from([68, 73, 68, 76, 0, 0]);
-}
-
-$query;
-export function printBlob(blob: blob): blob {
- console.log(typeof blob);
- return blob;
-}
+import { Canister, blob, query } from 'azle';
+
+export default Canister({
+ getBlob: query([], blob, () => {
+ return Uint8Array.from([68, 73, 68, 76, 0, 0]);
+ }),
+ printBlob: query([blob], blob, (blob) => {
+ console.log(typeof blob);
+ return blob;
+ })
+});
Candid:
service : () -> {
@@ -1051,18 +1065,17 @@
The Azle type nat
corresponds to the Candid type nat and will become a JavaScript BigInt at runtime.
TypeScript:
-import { nat, $query } from 'azle';
-
-$query;
-export function getNat(): nat {
- return 340_282_366_920_938_463_463_374_607_431_768_211_455n;
-}
-
-$query;
-export function printNat(nat: nat): nat {
- console.log(typeof nat);
- return nat;
-}
+import { Canister, nat, query } from 'azle';
+
+export default Canister({
+ getNat: query([], nat, () => {
+ return 340_282_366_920_938_463_463_374_607_431_768_211_455n;
+ }),
+ printNat: query([nat], nat, (nat) => {
+ console.log(typeof nat);
+ return nat;
+ })
+});
Candid:
service : () -> {
@@ -1077,18 +1090,17 @@
The Azle type nat64
corresponds to the Candid type nat64 and will become a JavaScript BigInt at runtime.
TypeScript:
-import { nat64, $query } from 'azle';
-
-$query;
-export function getNat64(): nat64 {
- return 18_446_744_073_709_551_615n;
-}
-
-$query;
-export function printNat64(nat64: nat64): nat64 {
- console.log(typeof nat64);
- return nat64;
-}
+import { Canister, nat64, query } from 'azle';
+
+export default Canister({
+ getNat64: query([], nat64, () => {
+ return 18_446_744_073_709_551_615n;
+ }),
+ printNat64: query([nat64], nat64, (nat64) => {
+ console.log(typeof nat64);
+ return nat64;
+ })
+});
Candid:
service : () -> {
@@ -1103,18 +1115,17 @@
The Azle type nat32
corresponds to the Candid type nat32 and will become a JavaScript Number at runtime.
TypeScript:
-import { nat32, $query } from 'azle';
-
-$query;
-export function getNat32(): nat32 {
- return 4_294_967_295;
-}
-
-$query;
-export function printNat32(nat32: nat32): nat32 {
- console.log(typeof nat32);
- return nat32;
-}
+import { Canister, nat32, query } from 'azle';
+
+export default Canister({
+ getNat32: query([], nat32, () => {
+ return 4_294_967_295;
+ }),
+ printNat32: query([nat32], nat32, (nat32) => {
+ console.log(typeof nat32);
+ return nat32;
+ })
+});
Candid:
service : () -> {
@@ -1129,18 +1140,17 @@
The Azle type nat16
corresponds to the Candid type nat16 and will become a JavaScript Number at runtime.
TypeScript:
-import { nat16, $query } from 'azle';
-
-$query;
-export function getNat16(): nat16 {
- return 65_535;
-}
-
-$query;
-export function printNat16(nat16: nat16): nat16 {
- console.log(typeof nat16);
- return nat16;
-}
+import { Canister, nat16, query } from 'azle';
+
+export default Canister({
+ getNat16: query([], nat16, () => {
+ return 65_535;
+ }),
+ printNat16: query([nat16], nat16, (nat16) => {
+ console.log(typeof nat16);
+ return nat16;
+ })
+});
Candid:
service : () -> {
@@ -1155,18 +1165,17 @@
The Azle type nat8
corresponds to the Candid type nat8 and will become a JavaScript Number at runtime.
TypeScript:
-import { nat8, $query } from 'azle';
-
-$query;
-export function getNat8(): nat8 {
- return 255;
-}
-
-$query;
-export function printNat8(nat8: nat8): nat8 {
- console.log(typeof nat8);
- return nat8;
-}
+import { Canister, nat8, query } from 'azle';
+
+export default Canister({
+ getNat8: query([], nat8, () => {
+ return 255;
+ }),
+ printNat8: query([nat8], nat8, (nat8) => {
+ console.log(typeof nat8);
+ return nat8;
+ })
+});
Candid:
service : () -> {
@@ -1181,18 +1190,17 @@
The Azle type int
corresponds to the Candid type int and will become a JavaScript BigInt at runtime.
TypeScript:
-import { int, $query } from 'azle';
-
-$query;
-export function getInt(): int {
- return 170_141_183_460_469_231_731_687_303_715_884_105_727n;
-}
-
-$query;
-export function printInt(int: int): int {
- console.log(typeof int);
- return int;
-}
+import { Canister, int, query } from 'azle';
+
+export default Canister({
+ getInt: query([], int, () => {
+ return 170_141_183_460_469_231_731_687_303_715_884_105_727n;
+ }),
+ printInt: query([int], int, (int) => {
+ console.log(typeof int);
+ return int;
+ })
+});
Candid:
service : () -> {
@@ -1207,18 +1215,17 @@
The Azle type int64
corresponds to the Candid type int64 and will become a JavaScript BigInt at runtime.
TypeScript:
-import { int64, $query } from 'azle';
-
-$query;
-export function getInt64(): int64 {
- return 9_223_372_036_854_775_807n;
-}
-
-$query;
-export function printInt64(int64: int64): int64 {
- console.log(typeof int64);
- return int64;
-}
+import { Canister, int64, query } from 'azle';
+
+export default Canister({
+ getInt64: query([], int64, () => {
+ return 9_223_372_036_854_775_807n;
+ }),
+ printInt64: query([int64], int64, (int64) => {
+ console.log(typeof int64);
+ return int64;
+ })
+});
Candid:
service : () -> {
@@ -1233,18 +1240,17 @@
The Azle type int32
corresponds to the Candid type int32 and will become a JavaScript Number at runtime.
TypeScript:
-import { int32, $query } from 'azle';
-
-$query;
-export function getInt32(): int32 {
- return 2_147_483_647;
-}
-
-$query;
-export function printInt32(int32: int32): int32 {
- console.log(typeof int32);
- return int32;
-}
+import { Canister, int32, query } from 'azle';
+
+export default Canister({
+ getInt32: query([], int32, () => {
+ return 2_147_483_647;
+ }),
+ printInt32: query([int32], int32, (int32) => {
+ console.log(typeof int32);
+ return int32;
+ })
+});
Candid:
service : () -> {
@@ -1259,18 +1265,17 @@
The Azle type int16
corresponds to the Candid type int16 and will become a JavaScript Number at runtime.
TypeScript:
-import { int16, $query } from 'azle';
-
-$query;
-export function getInt16(): int16 {
- return 32_767;
-}
-
-$query;
-export function printInt16(int16: int16): int16 {
- console.log(typeof int16);
- return int16;
-}
+import { Canister, int16, query } from 'azle';
+
+export default Canister({
+ getInt16: query([], int16, () => {
+ return 32_767;
+ }),
+ printInt16: query([int16], int16, (int16) => {
+ console.log(typeof int16);
+ return int16;
+ })
+});
Candid:
service : () -> {
@@ -1285,18 +1290,17 @@
The Azle type int8
corresponds to the Candid type int8 and will become a JavaScript Number at runtime.
TypeScript:
-import { int8, $query } from 'azle';
-
-$query;
-export function getInt8(): int8 {
- return 127;
-}
-
-$query;
-export function printInt8(int8: int8): int8 {
- console.log(typeof int8);
- return int8;
-}
+import { Canister, int8, query } from 'azle';
+
+export default Canister({
+ getInt8: query([], int8, () => {
+ return 127;
+ }),
+ printInt8: query([int8], int8, (int8) => {
+ console.log(typeof int8);
+ return int8;
+ })
+});
Candid:
service : () -> {
@@ -1311,18 +1315,17 @@
The Azle type float64
and the TypeScript type number
both correspond to the Candid type float64 and will become a JavaScript Number at runtime.
TypeScript:
-import { float64, $query } from 'azle';
-
-$query;
-export function getFloat64(): float64 {
- return Math.E;
-}
-
-$query;
-export function printFloat64(float64: float64): float64 {
- console.log(typeof float64);
- return float64;
-}
+import { Canister, float64, query } from 'azle';
+
+export default Canister({
+ getFloat64: query([], float64, () => {
+ return Math.E;
+ }),
+ printFloat64: query([float64], float64, (float64) => {
+ console.log(typeof float64);
+ return float64;
+ })
+});
Candid:
service : () -> {
@@ -1337,18 +1340,17 @@
The Azle type float32
corresponds to the Candid type float32 and will become a JavaScript Number at runtime.
TypeScript:
-import { float32, $query } from 'azle';
-
-$query;
-export function getFloat32(): float32 {
- return Math.PI;
-}
-
-$query;
-export function printFloat32(float32: float32): float32 {
- console.log(typeof float32);
- return float32;
-}
+import { Canister, float32, query } from 'azle';
+
+export default Canister({
+ getFloat32: query([], float32, () => {
+ return Math.PI;
+ }),
+ printFloat32: query([float32], float32, (float32) => {
+ console.log(typeof float32);
+ return float32;
+ })
+});
Candid:
service : () -> {
@@ -1363,18 +1365,17 @@
The TypeScript type boolean
corresponds to the Candid type bool and will become a JavaScript Boolean at runtime.
TypeScript:
-import { $query } from 'azle';
-
-$query;
-export function getBool(): boolean {
- return true;
-}
-
-$query;
-export function printBool(bool: boolean): boolean {
- console.log(typeof bool);
- return bool;
-}
+import { Canister, bool, query } from 'azle';
+
+export default Canister({
+ getBool: query([], bool, () => {
+ return true;
+ }),
+ printBool: query([bool], bool, (bool) => {
+ console.log(typeof bool);
+ return bool;
+ })
+});
Candid:
service : () -> {
@@ -1389,18 +1390,17 @@
The TypeScript type null
corresponds to the Candid type null and will become a JavaScript null at runtime.
TypeScript:
-import { $query } from 'azle';
-
-$query;
-export function getNull(): null {
- return null;
-}
-
-$query;
-export function printNull(null_: null): null {
- console.log(typeof null_);
- return null_;
-}
+import { Canister, Null, query } from 'azle';
+
+export default Canister({
+ getNull: query([], Null, () => {
+ return null;
+ }),
+ printNull: query([Null], Null, (null_) => {
+ console.log(typeof null_);
+ return null_;
+ })
+});
Candid:
service : () -> {
@@ -1415,18 +1415,17 @@
The Azle type Vec
corresponds to the Candid type vec and will become a JavaScript array of the specified type at runtime (except for Vec<nat8>
which will become a Uint8Array
, thus it is recommended to use the blob
type instead of Vec<nat8>
).
TypeScript:
-import { int32, $query, Vec } from 'azle';
-
-$query;
-export function getNumbers(): Vec<int32> {
- return [0, 1, 2, 3];
-}
-
-$query;
-export function printNumbers(numbers: Vec<int32>): Vec<int32> {
- console.log(typeof numbers);
- return numbers;
-}
+import { Canister, Vec, int32, query } from 'azle';
+
+export default Canister({
+ getNumbers: query([], Vec(int32), () => {
+ return [0, 1, 2, 3];
+ }),
+ printNumbers: query([Vec(int32)], Vec(int32), (numbers) => {
+ console.log(typeof numbers);
+ return numbers;
+ })
+});
Candid:
service : () -> {
@@ -1441,17 +1440,16 @@
The Azle type Opt
corresponds to the Candid type opt . It is a variant with Some
and None
cases. At runtime if the value of the variant is Some
, the Some
property of the variant object will have a value of the enclosed Opt
type at runtime.
TypeScript:
-import { Opt, $query } from 'azle';
-
-$query;
-export function getOptSome(): Opt<boolean> {
- return Opt.Some(true);
-}
+import { Canister, None, Opt, Some, bool, query } from 'azle';
-$query;
-export function getOptNone(): Opt<boolean> {
- return Opt.None;
-}
+export default Canister({
+ getOptSome: query([], Opt(bool), () => {
+ return Some(true);
+ }),
+ getOptNone: query([], Opt(bool), () => {
+ return None;
+ })
+});
Candid:
service : () -> {
@@ -1469,26 +1467,25 @@
TypeScript type aliases referring to object literals wrapped in the Record
Azle type correspond to the Candid record type and will become JavaScript Objects at runtime.
TypeScript:
-import { Principal, $query, Record } from 'azle';
+import { Canister, Principal, Record, query, text } from 'azle';
-type User = Record<{
- id: Principal;
- username: string;
-}>;
+const User = Record({
+ id: Principal,
+ username: text
+});
-$query;
-export function getUser(): User {
- return {
- id: Principal.fromUint8Array(Uint8Array.from([0])),
- username: 'lastmjs'
- };
-}
-
-$query;
-export function printUser(user: User): User {
- console.log(typeof user);
- return user;
-}
+export default Canister({
+ getUser: query([], User, () => {
+ return {
+ id: Principal.fromUint8Array(Uint8Array.from([0])),
+ username: 'lastmjs'
+ };
+ }),
+ printUser: query([User], User, (user) => {
+ console.log(typeof user);
+ return user;
+ })
+});
Candid:
type User = record { id : principal; username : text };
@@ -1504,32 +1501,31 @@
TypeScript type aliases referring to object literals wrapped in the Variant
Azle type correspond to the Candid variant type and will become JavaScript Objects at runtime.
TypeScript:
-import { $query, Variant } from 'azle';
-
-type Reaction = Variant<{
- Fire: null;
- ThumbsUp: null;
- Emotion: Emotion;
-}>;
-
-type Emotion = Variant<{
- Happy: null;
- Indifferent: null;
- Sad: null;
-}>;
-
-$query;
-export function getReaction(): Reaction {
- return {
- Fire: null
- };
-}
-
-$query;
-export function printReaction(reaction: Reaction): Reaction {
- console.log(typeof reaction);
- return reaction;
-}
+import { Canister, Null, Variant, query } from 'azle';
+
+const Emotion = Variant({
+ Happy: Null,
+ Indifferent: Null,
+ Sad: Null
+});
+
+const Reaction = Variant({
+ Fire: Null,
+ ThumbsUp: Null,
+ Emotion: Emotion
+});
+
+export default Canister({
+ getReaction: query([], Reaction, () => {
+ return {
+ Fire: null
+ };
+ }),
+ printReaction: query([Reaction], Reaction, (reaction) => {
+ console.log(typeof reaction);
+ return reaction;
+ })
+});
Candid:
type Emotion = variant { Sad; Indifferent; Happy };
@@ -1548,20 +1544,22 @@
The first element is an @dfinity/principal and the second is a JavaScript string . The @dfinity/principal
represents the principal
of the canister/service where the function exists, and the string
represents the function's name.
A func
acts as a callback, allowing the func
receiver to know which canister instance and method must be used to call back.
TypeScript:
-import { Func, Principal, $query, Query } from 'azle';
-
-type BasicFunc = Func<Query<(param1: string) => string>>;
+import { Canister, Func, Principal, query, text } from 'azle';
-$query;
-export function getBasicFunc(): BasicFunc {
- return [Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), 'getBasicFunc'];
-}
+const BasicFunc = Func([text], text, 'query');
-$query;
-export function printBasicFunc(basicFunc: BasicFunc): BasicFunc {
- console.log(typeof basicFunc);
- return basicFunc;
-}
+export default Canister({
+ getBasicFunc: query([], BasicFunc, () => {
+ return [
+ Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'),
+ 'getBasicFunc'
+ ];
+ }),
+ printBasicFunc: query([BasicFunc], BasicFunc, (basicFunc) => {
+ console.log(typeof basicFunc);
+ return basicFunc;
+ })
+});
Candid:
service : () -> {
@@ -1578,36 +1576,21 @@
JavaScript classes that inherit from the Azle type Service
correspond to the Candid service type and will become child classes capable of creating instances that can perform cross-canister calls at runtime.
TypeScript:
-import {
- CallResult,
- Principal,
- $query,
- Result,
- Service,
- serviceQuery,
- serviceUpdate,
- $update
-} from 'azle';
-
-class SomeService extends Service {
- @serviceQuery
- query1: () => CallResult<boolean>;
-
- @serviceUpdate
- update1: () => CallResult<string>;
-}
-
-$query;
-export function getService(): SomeService {
- return new SomeService(Principal.fromText('aaaaa-aa'));
-}
-
-$update;
-export async function callService(
- service: SomeService
-): Promise<Result<string, string>> {
- return await service.update1().call();
-}
+import { Canister, Principal, bool, ic, query, text, update } from 'azle';
+
+const SomeCanister = Canister({
+ query1: query([], bool),
+ update1: update([], text)
+});
+
+export default Canister({
+ getService: query([], SomeCanister, () => {
+ return SomeCanister(Principal.fromText('aaaaa-aa'));
+ }),
+ callService: update([SomeCanister], text, (service) => {
+ return ic.call(service.update1);
+ })
+});
Candid:
type ManualReply = variant { Ok : text; Err : text };
@@ -1627,18 +1610,17 @@
The Azle type Principal
corresponds to the Candid type principal and will become an @dfinity/principal at runtime.
TypeScript:
-import { Principal, $query } from 'azle';
-
-$query;
-export function getPrincipal(): Principal {
- return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai');
-}
-
-$query;
-export function printPrincipal(principal: Principal): Principal {
- console.log(typeof principal);
- return principal;
-}
+import { Canister, Principal, query } from 'azle';
+
+export default Canister({
+ getPrincipal: query([], Principal, () => {
+ return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai');
+ }),
+ printPrincipal: query([Principal], Principal, (principal) => {
+ console.log(typeof principal);
+ return principal;
+ })
+});
Candid:
service : () -> {
@@ -1653,18 +1635,17 @@
The Azle type reserved
corresponds to the Candid type reserved , is the TypeScript type any
, and will become a JavaScript null at runtime.
TypeScript:
-import { $query, reserved } from 'azle';
-
-$query;
-export function getReserved(): reserved {
- return 'anything';
-}
-
-$query;
-export function printReserved(reserved: reserved): reserved {
- console.log(typeof reserved);
- return reserved;
-}
+import { Canister, query, reserved } from 'azle';
+
+export default Canister({
+ getReserved: query([], reserved, () => {
+ return 'anything';
+ }),
+ printReserved: query([reserved], reserved, (reserved) => {
+ console.log(typeof reserved);
+ return reserved;
+ })
+});
Candid:
service : () -> {
@@ -1679,20 +1660,19 @@
The Azle type empty
corresponds to the Candid type empty and has no JavaScript value at runtime.
TypeScript:
-import { empty, $query } from 'azle';
-
-$query;
-export function getEmpty(): empty {
- throw 'Anything you want';
-}
-
-// Note: It is impossible to call this function because it requires an argument
-// but there is no way to pass an "empty" value as an argument.
-$query;
-export function printEmpty(empty: empty): empty {
- console.log(typeof empty);
- throw 'Anything you want';
-}
+import { Canister, empty, query } from 'azle';
+
+export default Canister({
+ getEmpty: query([], empty, () => {
+ throw 'Anything you want';
+ }),
+ // Note: It is impossible to call this function because it requires an argument
+ // but there is no way to pass an "empty" value as an argument.
+ printEmpty: query([empty], empty, (empty) => {
+ console.log(typeof empty);
+ throw 'Anything you want';
+ })
+});
Candid:
service : () -> {
@@ -1713,8 +1693,6 @@
Persistent across upgrades
Familiar API
Must specify memory id
-Must specify maximum key size
-Must specify maximum value size
No migrations per memory id
Stable structures are data structures with familiar APIs that allow access to stable memory. Stable memory is a separate memory location from the heap that currently allows up to 64 GiB of storage. Stable memory persists automatically across upgrades.
@@ -1722,184 +1700,168 @@
On the other hand, anything stored in stable memory will be preserved. Writing and reading to and from stable memory can be done with a low-level API , but it is generally easier and preferable to use stable structures.
Azle currently provides one stable structure called StableBTreeMap
. It's similar to a JavaScript Map and has most of the common operations you'd expect such as reading, inserting, and removing values.
Here's how to define a simple StableBTreeMap
. Each StableBTreeMap
must be defined in the global scope (not within any functions or objects etc):
-import { nat8, StableBTreeMap } from 'azle';
+import { StableBTreeMap, nat8, text } from 'azle';
-let map = new StableBTreeMap<nat8, string>(0, 100, 1_000);
+let map = StableBTreeMap(nat8, text, 0);
-This is a StableBTreeMap
with a key of type nat8
and a value of type string
. Key and value types can be any Candid type .
-This StableBTreeMap
also has a memory id
of 0
, a maximum key size of 100
bytes and a maximum value size of 1_000
bytes. You must statically specify the memory id
, maximum key size, and maximum value sizes (they cannot be variables).
-Each StableBTreeMap
instance must have a unique memory id
. Once a memory id
is allocated, it cannot be used with a different StableBTreeMap
. This means you can't create another StableBTreeMap
using the same memory id
, and you can't change the key or value types of an existing StableBTreeMap
. This problem will be addressed .
+This is a StableBTreeMap
with a key of type nat8
and a value of type text
. Key and value types can be any Candid type .
+This StableBTreeMap
also has a memory id
of 0
. Each StableBTreeMap
instance must have a unique memory id
. Once a memory id
is allocated, it cannot be used with a different StableBTreeMap
. This means you can't create another StableBTreeMap
using the same memory id
, and you can't change the key or value types of an existing StableBTreeMap
. This problem will be addressed .
Here's an example showing all of the basic StableBTreeMap
operations:
import {
- Alias,
- nat64,
- nat8,
+ Canister,
Opt,
- $query,
StableBTreeMap,
Tuple,
- $update,
- Vec
+ Vec,
+ bool,
+ nat64,
+ nat8,
+ query,
+ text,
+ update
} from 'azle';
-type Key = Alias<nat8>;
-type Value = Alias<string>;
+const Key = nat8;
+const Value = text;
-let map = new StableBTreeMap<Key, Value>(0, 100, 1_000);
+let map = StableBTreeMap(Key, Value, 0);
-$query;
-export function containsKey(key: Key): boolean {
- return map.containsKey(key);
-}
+export default Canister({
+ containsKey: query([Key], bool, (key) => {
+ return map.containsKey(key);
+ }),
-$query;
-export function get(key: Key): Opt<Value> {
- return map.get(key);
-}
+ get: query([Key], Opt(Value), (key) => {
+ return map.get(key);
+ }),
-$update;
-export function insert(key: Key, value: Value): Opt<Value> {
- return map.insert(key, value);
-}
+ insert: update([Key, Value], Opt(Value), (key, value) => {
+ return map.insert(key, value);
+ }),
-$query;
-export function isEmpty(): boolean {
- return map.isEmpty();
-}
+ isEmpty: query([], bool, () => {
+ return map.isEmpty();
+ }),
-$query;
-export function items(): Vec<Tuple<[Key, Value]>> {
- return map.items();
-}
+ items: query([], Vec(Tuple(Key, Value)), () => {
+ return map.items();
+ }),
-$query;
-export function keys(): Vec<Key> {
- return map.keys();
-}
+ keys: query([], Vec(Key), () => {
+ return map.keys();
+ }),
-$query;
-export function len(): nat64 {
- return map.len();
-}
+ len: query([], nat64, () => {
+ return map.len();
+ }),
-$update;
-export function remove(key: Key): Opt<Value> {
- return map.remove(key);
-}
+ remove: update([Key], Opt(Value), (key) => {
+ return map.remove(key);
+ }),
-$query;
-export function values(): Vec<Value> {
- return map.values();
-}
+ values: query([], Vec(Value), () => {
+ return map.values();
+ })
+});
With these basic operations you can build more complex CRUD database applications:
import {
blob,
+ Canister,
ic,
+ Err,
nat64,
+ Ok,
Opt,
Principal,
- $query,
+ query,
Record,
Result,
StableBTreeMap,
- $update,
+ text,
+ update,
Variant,
- Vec,
- match
+ Vec
} from 'azle';
-type User = Record<{
- id: Principal;
- createdAt: nat64;
- recordingIds: Vec<Principal>;
- username: string;
-}>;
+const User = Record({
+ id: Principal,
+ createdAt: nat64,
+ recordingIds: Vec(Principal),
+ username: text
+});
-type Recording = Record<{
- id: Principal;
- audio: blob;
- createdAt: nat64;
- name: string;
- userId: Principal;
-}>;
-
-let users = new StableBTreeMap<Principal, User>(0, 38, 100_000);
-let recordings = new StableBTreeMap<Principal, Recording>(1, 38, 5_000_000);
-
-$update;
-export function createUser(username: string): User {
- const id = generateId();
- const user: User = {
- id,
- createdAt: ic.time(),
- recordingIds: [],
- username
- };
-
- users.insert(user.id, user);
-
- return user;
-}
+const Recording = Record({
+ id: Principal,
+ audio: blob,
+ createdAt: nat64,
+ name: text,
+ userId: Principal
+});
+
+const AudioRecorderError = Variant({
+ RecordingDoesNotExist: Principal,
+ UserDoesNotExist: Principal
+});
+
+let users = StableBTreeMap(Principal, User, 0);
+let recordings = StableBTreeMap(Principal, Recording, 1);
+
+export default Canister({
+ createUser: update([text], User, (username) => {
+ const id = generateId();
+ const user: typeof User = {
+ id,
+ createdAt: ic.time(),
+ recordingIds: [],
+ username
+ };
-$query;
-export function readUsers(): Vec<User> {
- return users.values();
-}
+ users.insert(user.id, user);
+
+ return user;
+ }),
+ readUsers: query([], Vec(User), () => {
+ return users.values();
+ }),
+ readUserById: query([Principal], Opt(User), (id) => {
+ return users.get(id);
+ }),
+ deleteUser: update([Principal], Result(User, AudioRecorderError), (id) => {
+ const userOpt = users.get(id);
+
+ if (userOpt.length === 0) {
+ return Err({
+ UserDoesNotExist: id
+ });
+ }
-$query;
-export function readUserById(id: Principal): Opt<User> {
- return users.get(id);
-}
+ const user = userOpt[0];
-$update;
-export function deleteUser(id: Principal): Result<
- User,
- Variant<{
- UserDoesNotExist: Principal;
- }>
-> {
- const user = users.get(id);
+ user.recordingIds.forEach((recordingId) => {
+ recordings.remove(recordingId);
+ });
- return match(user, {
- Some: (user) => {
- user.recordingIds.forEach((recordingId) => {
- recordings.remove(recordingId);
- });
+ users.remove(user.id);
- users.remove(user.id);
+ return Ok(user);
+ }),
+ createRecording: update(
+ [blob, text, Principal],
+ Result(Recording, AudioRecorderError),
+ (audio, name, userId) => {
+ const userOpt = users.get(userId);
- return {
- Ok: user
- };
- },
- None: () => {
- return {
- Err: {
- UserDoesNotExist: id
- }
- };
- }
- });
-}
+ if (userOpt.length === 0) {
+ return Err({
+ UserDoesNotExist: userId
+ });
+ }
-$update;
-export function createRecording(
- audio: blob,
- name: string,
- userId: Principal
-): Result<
- Recording,
- Variant<{
- UserDoesNotExist: Principal;
- }>
-> {
- const user = users.get(userId);
+ const user = userOpt[0];
- return match(user, {
- Some: (user) => {
const id = generateId();
- const recording: Recording = {
+ const recording: typeof Recording = {
id,
audio,
createdAt: ic.time(),
@@ -1909,87 +1871,60 @@
recordings.insert(recording.id, recording);
- const updatedUser: User = {
+ const updatedUser: typeof User = {
...user,
recordingIds: [...user.recordingIds, recording.id]
};
users.insert(updatedUser.id, updatedUser);
- return {
- Ok: recording
- };
- },
- None: () => {
- return {
- Err: {
- UserDoesNotExist: userId
- }
- };
+ return Ok(recording);
}
- });
-}
+ ),
+ readRecordings: query([], Vec(Recording), () => {
+ return recordings.values();
+ }),
+ readRecordingById: query([Principal], Opt(Recording), (id) => {
+ return recordings.get(id);
+ }),
+ deleteRecording: update(
+ [Principal],
+ Result(Recording, AudioRecorderError),
+ (id) => {
+ const recordingOpt = recordings.get(id);
+
+ if (recordingOpt.length === 0) {
+ return Err({ RecordingDoesNotExist: id });
+ }
-$query;
-export function readRecordings(): Vec<Recording> {
- return recordings.values();
-}
+ const recording = recordingOpt[0];
-$query;
-export function readRecordingById(id: Principal): Opt<Recording> {
- return recordings.get(id);
-}
+ const userOpt = users.get(recording.userId);
-$update;
-export function deleteRecording(id: Principal): Result<
- Recording,
- Variant<{
- RecordingDoesNotExist: Principal;
- UserDoesNotExist: Principal;
- }>
-> {
- const recording = recordings.get(id);
-
- return match(recording, {
- Some: (recording) => {
- const user = users.get(recording.userId);
-
- return match(user, {
- Some: (user) => {
- const updatedUser: User = {
- ...user,
- recordingIds: user.recordingIds.filter(
- (recordingId) =>
- recordingId.toText() !== recording.id.toText()
- )
- };
-
- users.insert(updatedUser.id, updatedUser);
-
- recordings.remove(id);
-
- return {
- Ok: recording
- };
- },
- None: () => {
- return {
- Err: {
- UserDoesNotExist: recording.userId
- }
- };
- }
- });
- },
- None: () => {
- return {
- Err: {
- RecordingDoesNotExist: id
- }
+ if (userOpt.length === 0) {
+ return Err({
+ UserDoesNotExist: recording.userId
+ });
+ }
+
+ const user = userOpt[0];
+
+ const updatedUser: typeof User = {
+ ...user,
+ recordingIds: user.recordingIds.filter(
+ (recordingId) =>
+ recordingId.toText() !== recording.id.toText()
+ )
};
+
+ users.insert(updatedUser.id, updatedUser);
+
+ recordings.remove(id);
+
+ return Ok(recording);
}
- });
-}
+ )
+});
function generateId(): Principal {
const randomBytes = new Array(29)
@@ -2001,25 +1936,33 @@
The example above shows a very basic audio recording backend application. There are two types of entities that need to be stored, User
and Recording
. These are represented as Candid
records.
Each entity gets its own StableBTreeMap
:
-import { blob, nat64, Principal, Record, StableBTreeMap, Vec } from 'azle';
+import {
+ Principal,
+ Record,
+ StableBTreeMap,
+ Vec,
+ blob,
+ nat64,
+ text
+} from 'azle';
-type User = Record<{
- id: Principal;
- createdAt: nat64;
- recordingIds: Vec<Principal>;
- username: string;
-}>;
+const User = Record({
+ id: Principal,
+ createdAt: nat64,
+ recordingIds: Vec(Principal),
+ username: text
+});
-type Recording = Record<{
- id: Principal;
- audio: blob;
- createdAt: nat64;
- name: string;
- userId: Principal;
-}>;
+const Recording = Record({
+ id: Principal,
+ audio: blob,
+ createdAt: nat64,
+ name: text,
+ userId: Principal
+});
-let users = new StableBTreeMap<Principal, User>(0, 38, 100_000);
-let recordings = new StableBTreeMap<Principal, Recording>(1, 38, 5_000_000);
+let users = StableBTreeMap(Principal, User, 0);
+let recordings = StableBTreeMap(Principal, Recording, 1);
Notice that each StableBTreeMap
has a unique memory id
. The maximum key and value sizes are also set according to the expected application usage.
You can figure out the appropriate maximum key and value sizes by reasoning about your application and engaging in some trial and error using the insert
method. Calling insert
on a StableBTreeMap
will throw an error which in some cases will have the information that you need to determine the maximum key or value size.
@@ -2380,7 +2323,6 @@ outgoing_http_requests
import {
- Alias,
ic,
$init,
match,
@@ -2398,7 +2340,7 @@
stable_structures
import {
- Alias,
nat64,
nat8,
Opt,
@@ -5240,8 +5181,8 @@
Vec
} from 'azle';
-type Key = Alias<nat8>;
-type Value = Alias<string>;
+type Key = nat8;
+type Value = string;
let map = new StableBTreeMap<Key, Value>(0, 100, 1_000);
diff --git a/the_azle_book/book/searchindex.js b/the_azle_book/book/searchindex.js
index 4c696765c5..d3ed1fbf15 100644
--- a/the_azle_book/book/searchindex.js
+++ b/the_azle_book/book/searchindex.js
@@ -1 +1 @@
-Object.assign(window.search, {"doc_urls":["the_azle_book.html#the-azle-book","azle.html#azle-beta","azle.html#disclaimer","azle.html#roadmap","azle.html#demergent-labs","azle.html#benefits-and-drawbacks","azle.html#benefits","azle.html#drawbacks","installation.html#installation","installation.html#nodejs","installation.html#dfx","hello_world.html#hello-world","hello_world.html#quick-start","hello_world.html#methodical-start","hello_world.html#the-project-directory-and-file-structure","hello_world.html#indexts","hello_world.html#tsconfigjson","hello_world.html#dfxjson","hello_world.html#local-deployment","hello_world.html#interacting-with-your-canister-from-the-command-line","hello_world.html#interacting-with-your-canister-from-the-web-ui","deployment.html#deployment","deployment.html#starting-the-local-replica","deployment.html#deploying-to-the-local-replica","deployment.html#interacting-with-your-canister","deployment.html#dfx-command-line","deployment.html#dfx-web-ui","deployment.html#dfinityagent","deployment.html#deploying-to-mainnet","examples.html#examples","internet_computer_overview.html#internet-computer-overview","canisters_overview.html#canisters-overview","query_methods.html#query-methods","query_methods.html#tldr","update_methods.html#update-methods","update_methods.html#tldr","candid.html#candid","candid.html#text","candid.html#blob","candid.html#nat","candid.html#nat64","candid.html#nat32","candid.html#nat16","candid.html#nat8","candid.html#int","candid.html#int64","candid.html#int32","candid.html#int16","candid.html#int8","candid.html#float64","candid.html#float32","candid.html#bool","candid.html#null","candid.html#vec","candid.html#opt","candid.html#record","candid.html#variant","candid.html#func","candid.html#service","candid.html#principal","candid.html#reserved","candid.html#empty","stable_structures.html#stable-structures","stable_structures.html#tldr","stable_structures.html#caveats","stable_structures.html#keys","cross_canister.html#cross-canister","http.html#http","http.html#incoming-http-requests","http.html#outgoing-http-requests","management_canister.html#management-canister","canister_lifecycle.html#canister-lifecycle","timers.html#timers","cycles.html#cycles","caveats.html#caveats","caveats.html#unknown-security-vulnerabilities","caveats.html#high-instructioncycle-count","caveats.html#javascript-syntax-and-features","caveats.html#npm-packages","caveats.html#promises","caveats.html#treat-azle-types-as-unique-keywords","reference/reference.html#reference","reference/bitcoin.html#bitcoin","reference/bitcoin.html#tecdsa","reference/bitcoin.html#bitcoin-integration","reference/bitcoin.html#ckbtc","reference/call_apis/call_apis.html#call-apis","reference/call_apis/accept_message.html#accept-message","reference/call_apis/arg_data_raw.html#arg-data-raw","reference/call_apis/arg_data_raw_size.html#arg-data-raw-size","reference/call_apis/call.html#call","reference/call_apis/call_raw.html#call-raw","reference/call_apis/call_raw128.html#call-raw-128","reference/call_apis/call_with_payment.html#call-with-payment","reference/call_apis/call_with_payment128.html#call-with-payment-128","reference/call_apis/caller.html#caller","reference/call_apis/method_name.html#method-name","reference/call_apis/msg_cycles_accept.html#msg-cycles-accept","reference/call_apis/msg_cycles_accept128.html#msg-cycles-accept-128","reference/call_apis/msg_cycles_available.html#msg-cycles-available","reference/call_apis/msg_cycles_available128.html#msg-cycles-available-128","reference/call_apis/msg_cycles_refunded.html#msg-cycles-refunded","reference/call_apis/msg_cycles_refunded128.html#msg-cycles-refunded-128","reference/call_apis/notify.html#notify","reference/call_apis/notify_raw.html#notify-raw","reference/call_apis/notify_with_payment_128.html#notify-with-payment-128","reference/call_apis/reject.html#reject","reference/call_apis/reject_code.html#reject-code","reference/call_apis/reject_message.html#reject-message","reference/call_apis/reply.html#reply","reference/call_apis/reply_raw.html#reply-raw","reference/candid/candid.html#candid","reference/candid/blob.html#blob","reference/candid/bool.html#bool","reference/candid/empty.html#empty","reference/candid/float32.html#float32","reference/candid/float64.html#float64","reference/candid/func.html#func","reference/candid/int.html#int","reference/candid/int8.html#int8","reference/candid/int16.html#int16","reference/candid/int32.html#int32","reference/candid/int64.html#int64","reference/candid/nat.html#nat","reference/candid/nat8.html#nat8","reference/candid/nat16.html#nat16","reference/candid/nat32.html#nat32","reference/candid/nat64.html#nat64","reference/candid/null.html#null","reference/candid/opt.html#opt","reference/candid/principal.html#principal","reference/candid/record.html#record","reference/candid/reserved.html#reserved","reference/candid/service.html#service","reference/candid/text.html#text","reference/candid/variant.html#variant","reference/candid/vec.html#vec","reference/canister_apis/canister_apis.html#canister-apis","reference/canister_apis/candid_decode.html#candid-decode","reference/canister_apis/candid_encode.html#candid-encode","reference/canister_apis/canister_balance.html#canister-balance","reference/canister_apis/canister_balance128.html#canister-balance-128","reference/canister_apis/canister_version.html#canister-version","reference/canister_apis/canister_id.html#canister-id","reference/canister_apis/data_certificate.html#data-certificate","reference/canister_apis/instruction_counter.html#instruction-counter","reference/canister_apis/is_controller.html#is-controller","reference/canister_apis/performance_counter.html#performance-counter","reference/canister_apis/print.html#print","reference/canister_apis/set_certified_data.html#set-certified-data","reference/canister_apis/time.html#time","reference/canister_apis/trap.html#trap","reference/canister_methods/canister_methods.html#canister-methods","reference/canister_methods/heartbeat.html#heartbeat","reference/canister_methods/http_request.html#http_request","reference/canister_methods/http_request_update.html#http_request_update","reference/canister_methods/init.html#init","reference/canister_methods/inspect_message.html#inspect-message","reference/canister_methods/post_upgrade.html#post-upgrade","reference/canister_methods/pre_upgrade.html#pre-upgrade","reference/canister_methods/query.html#query","reference/canister_methods/update.html#update","reference/environment_variables.html#environment-variables","reference/environment_variables.html#dfxjson","reference/environment_variables.html#processenv","reference/management_canister/management_canister.html#management-canister","reference/management_canister/bitcoin_get_balance.html#bitcoin_get_balance","reference/management_canister/bitcoin_get_current_fee_percentiles.html#bitcoin_get_current_fee_percentiles","reference/management_canister/bitcoin_get_utxos.html#bitcoin_get_utxos","reference/management_canister/bitcoin_send_transaction.html#bitcoin_send_transaction","reference/management_canister/canister_status.html#canister_status","reference/management_canister/create_canister.html#create_canister","reference/management_canister/delete_canister.html#delete_canister","reference/management_canister/deposit_cycles.html#deposit_cycles","reference/management_canister/ecdsa_public_key.html#ecdsa_public_key","reference/management_canister/http_request.html#http_request","reference/management_canister/install_code.html#install_code","reference/management_canister/provisional_create_canister_with_cycles.html#provisional_create_canister_with_cycles","reference/management_canister/provisional_top_up_canister.html#provisional_top_up_canister","reference/management_canister/raw_rand.html#raw_rand","reference/management_canister/sign_with_ecdsa.html#sign_with_ecdsa","reference/management_canister/start_canister.html#start_canister","reference/management_canister/stop_canister.html#stop_canister","reference/management_canister/uninstall_code.html#uninstall_code","reference/management_canister/update_settings.html#update_settings","reference/plugins.html#plugins","reference/plugins.html#local-plugin","reference/plugins.html#npm-plugin","reference/stable_memory/stable_memory.html#stable-memory","reference/stable_memory/stable_structures.html#stable-structures","reference/stable_memory/stable_bytes.html#stable-bytes","reference/stable_memory/stable_grow.html#stable-grow","reference/stable_memory/stable_read.html#stable-read","reference/stable_memory/stable_size.html#stable-size","reference/stable_memory/stable_write.html#stable-write","reference/stable_memory/stable64_grow.html#stable64-grow","reference/stable_memory/stable64_read.html#stable64-read","reference/stable_memory/stable64_size.html#stable64-size","reference/stable_memory/stable64_write.html#stable64-write","reference/timers/timers.html#timers","reference/timers/clear_timer.html#clear-timer","reference/timers/set_timer.html#set-timer","reference/timers/set_timer_interval.html#set-timer-interval","reference/wasm_binary_optimization.html#wasm-binary-optimization"],"index":{"documentStore":{"docInfo":{"0":{"body":189,"breadcrumbs":4,"title":2},"1":{"body":23,"breadcrumbs":4,"title":2},"10":{"body":34,"breadcrumbs":2,"title":1},"100":{"body":22,"breadcrumbs":11,"title":4},"101":{"body":44,"breadcrumbs":9,"title":3},"102":{"body":44,"breadcrumbs":11,"title":4},"103":{"body":31,"breadcrumbs":5,"title":1},"104":{"body":26,"breadcrumbs":7,"title":2},"105":{"body":19,"breadcrumbs":9,"title":3},"106":{"body":20,"breadcrumbs":5,"title":1},"107":{"body":41,"breadcrumbs":7,"title":2},"108":{"body":23,"breadcrumbs":7,"title":2},"109":{"body":27,"breadcrumbs":5,"title":1},"11":{"body":61,"breadcrumbs":4,"title":2},"110":{"body":55,"breadcrumbs":7,"title":2},"111":{"body":25,"breadcrumbs":3,"title":1},"112":{"body":77,"breadcrumbs":4,"title":1},"113":{"body":52,"breadcrumbs":4,"title":1},"114":{"body":89,"breadcrumbs":4,"title":1},"115":{"body":55,"breadcrumbs":4,"title":1},"116":{"body":59,"breadcrumbs":4,"title":1},"117":{"body":117,"breadcrumbs":4,"title":1},"118":{"body":55,"breadcrumbs":4,"title":1},"119":{"body":55,"breadcrumbs":4,"title":1},"12":{"body":29,"breadcrumbs":4,"title":2},"120":{"body":55,"breadcrumbs":4,"title":1},"121":{"body":55,"breadcrumbs":4,"title":1},"122":{"body":55,"breadcrumbs":4,"title":1},"123":{"body":55,"breadcrumbs":4,"title":1},"124":{"body":55,"breadcrumbs":4,"title":1},"125":{"body":55,"breadcrumbs":4,"title":1},"126":{"body":55,"breadcrumbs":4,"title":1},"127":{"body":55,"breadcrumbs":4,"title":1},"128":{"body":53,"breadcrumbs":4,"title":1},"129":{"body":67,"breadcrumbs":4,"title":1},"13":{"body":0,"breadcrumbs":4,"title":2},"130":{"body":66,"breadcrumbs":4,"title":1},"131":{"body":90,"breadcrumbs":4,"title":1},"132":{"body":56,"breadcrumbs":4,"title":1},"133":{"body":100,"breadcrumbs":4,"title":1},"134":{"body":59,"breadcrumbs":4,"title":1},"135":{"body":94,"breadcrumbs":4,"title":1},"136":{"body":89,"breadcrumbs":4,"title":1},"137":{"body":26,"breadcrumbs":5,"title":2},"138":{"body":24,"breadcrumbs":7,"title":2},"139":{"body":27,"breadcrumbs":7,"title":2},"14":{"body":43,"breadcrumbs":6,"title":4},"140":{"body":23,"breadcrumbs":7,"title":2},"141":{"body":23,"breadcrumbs":9,"title":3},"142":{"body":21,"breadcrumbs":7,"title":2},"143":{"body":24,"breadcrumbs":7,"title":2},"144":{"body":33,"breadcrumbs":7,"title":2},"145":{"body":25,"breadcrumbs":7,"title":2},"146":{"body":24,"breadcrumbs":5,"title":1},"147":{"body":17,"breadcrumbs":7,"title":2},"148":{"body":25,"breadcrumbs":5,"title":1},"149":{"body":23,"breadcrumbs":9,"title":3},"15":{"body":213,"breadcrumbs":3,"title":1},"150":{"body":21,"breadcrumbs":5,"title":1},"151":{"body":31,"breadcrumbs":5,"title":1},"152":{"body":12,"breadcrumbs":5,"title":2},"153":{"body":20,"breadcrumbs":5,"title":1},"154":{"body":90,"breadcrumbs":5,"title":1},"155":{"body":90,"breadcrumbs":5,"title":1},"156":{"body":25,"breadcrumbs":5,"title":1},"157":{"body":50,"breadcrumbs":7,"title":2},"158":{"body":18,"breadcrumbs":7,"title":2},"159":{"body":18,"breadcrumbs":7,"title":2},"16":{"body":18,"breadcrumbs":3,"title":1},"160":{"body":14,"breadcrumbs":5,"title":1},"161":{"body":23,"breadcrumbs":5,"title":1},"162":{"body":25,"breadcrumbs":5,"title":2},"163":{"body":40,"breadcrumbs":4,"title":1},"164":{"body":26,"breadcrumbs":4,"title":1},"165":{"body":19,"breadcrumbs":5,"title":2},"166":{"body":38,"breadcrumbs":5,"title":1},"167":{"body":34,"breadcrumbs":5,"title":1},"168":{"body":38,"breadcrumbs":5,"title":1},"169":{"body":53,"breadcrumbs":5,"title":1},"17":{"body":19,"breadcrumbs":3,"title":1},"170":{"body":42,"breadcrumbs":5,"title":1},"171":{"body":42,"breadcrumbs":5,"title":1},"172":{"body":40,"breadcrumbs":5,"title":1},"173":{"body":41,"breadcrumbs":5,"title":1},"174":{"body":57,"breadcrumbs":5,"title":1},"175":{"body":64,"breadcrumbs":5,"title":1},"176":{"body":51,"breadcrumbs":5,"title":1},"177":{"body":46,"breadcrumbs":5,"title":1},"178":{"body":44,"breadcrumbs":5,"title":1},"179":{"body":33,"breadcrumbs":5,"title":1},"18":{"body":14,"breadcrumbs":4,"title":2},"180":{"body":63,"breadcrumbs":5,"title":1},"181":{"body":40,"breadcrumbs":5,"title":1},"182":{"body":40,"breadcrumbs":5,"title":1},"183":{"body":40,"breadcrumbs":5,"title":1},"184":{"body":50,"breadcrumbs":5,"title":1},"185":{"body":40,"breadcrumbs":3,"title":1},"186":{"body":9,"breadcrumbs":4,"title":2},"187":{"body":12,"breadcrumbs":4,"title":2},"188":{"body":20,"breadcrumbs":5,"title":2},"189":{"body":107,"breadcrumbs":7,"title":2},"19":{"body":36,"breadcrumbs":6,"title":4},"190":{"body":17,"breadcrumbs":7,"title":2},"191":{"body":19,"breadcrumbs":7,"title":2},"192":{"body":22,"breadcrumbs":7,"title":2},"193":{"body":17,"breadcrumbs":7,"title":2},"194":{"body":21,"breadcrumbs":7,"title":2},"195":{"body":19,"breadcrumbs":7,"title":2},"196":{"body":22,"breadcrumbs":7,"title":2},"197":{"body":17,"breadcrumbs":7,"title":2},"198":{"body":21,"breadcrumbs":7,"title":2},"199":{"body":7,"breadcrumbs":3,"title":1},"2":{"body":32,"breadcrumbs":3,"title":1},"20":{"body":44,"breadcrumbs":6,"title":4},"200":{"body":17,"breadcrumbs":6,"title":2},"201":{"body":41,"breadcrumbs":6,"title":2},"202":{"body":42,"breadcrumbs":8,"title":3},"203":{"body":96,"breadcrumbs":7,"title":3},"21":{"body":39,"breadcrumbs":2,"title":1},"22":{"body":65,"breadcrumbs":4,"title":3},"23":{"body":12,"breadcrumbs":4,"title":3},"24":{"body":13,"breadcrumbs":3,"title":2},"25":{"body":59,"breadcrumbs":4,"title":3},"26":{"body":39,"breadcrumbs":4,"title":3},"27":{"body":21,"breadcrumbs":2,"title":1},"28":{"body":22,"breadcrumbs":3,"title":2},"29":{"body":51,"breadcrumbs":2,"title":1},"3":{"body":26,"breadcrumbs":3,"title":1},"30":{"body":137,"breadcrumbs":6,"title":3},"31":{"body":104,"breadcrumbs":4,"title":2},"32":{"body":0,"breadcrumbs":4,"title":2},"33":{"body":227,"breadcrumbs":3,"title":1},"34":{"body":0,"breadcrumbs":4,"title":2},"35":{"body":487,"breadcrumbs":3,"title":1},"36":{"body":395,"breadcrumbs":2,"title":1},"37":{"body":56,"breadcrumbs":2,"title":1},"38":{"body":74,"breadcrumbs":2,"title":1},"39":{"body":52,"breadcrumbs":2,"title":1},"4":{"body":20,"breadcrumbs":4,"title":2},"40":{"body":52,"breadcrumbs":2,"title":1},"41":{"body":52,"breadcrumbs":2,"title":1},"42":{"body":52,"breadcrumbs":2,"title":1},"43":{"body":52,"breadcrumbs":2,"title":1},"44":{"body":52,"breadcrumbs":2,"title":1},"45":{"body":52,"breadcrumbs":2,"title":1},"46":{"body":52,"breadcrumbs":2,"title":1},"47":{"body":52,"breadcrumbs":2,"title":1},"48":{"body":52,"breadcrumbs":2,"title":1},"49":{"body":56,"breadcrumbs":2,"title":1},"5":{"body":21,"breadcrumbs":4,"title":2},"50":{"body":52,"breadcrumbs":2,"title":1},"51":{"body":49,"breadcrumbs":2,"title":1},"52":{"body":50,"breadcrumbs":2,"title":1},"53":{"body":86,"breadcrumbs":2,"title":1},"54":{"body":64,"breadcrumbs":2,"title":1},"55":{"body":87,"breadcrumbs":2,"title":1},"56":{"body":91,"breadcrumbs":2,"title":1},"57":{"body":114,"breadcrumbs":2,"title":1},"58":{"body":97,"breadcrumbs":2,"title":1},"59":{"body":63,"breadcrumbs":2,"title":1},"6":{"body":861,"breadcrumbs":3,"title":1},"60":{"body":53,"breadcrumbs":2,"title":1},"61":{"body":86,"breadcrumbs":2,"title":1},"62":{"body":0,"breadcrumbs":4,"title":2},"63":{"body":841,"breadcrumbs":3,"title":1},"64":{"body":0,"breadcrumbs":3,"title":1},"65":{"body":28,"breadcrumbs":3,"title":1},"66":{"body":610,"breadcrumbs":4,"title":2},"67":{"body":3,"breadcrumbs":2,"title":1},"68":{"body":87,"breadcrumbs":4,"title":3},"69":{"body":163,"breadcrumbs":4,"title":3},"7":{"body":427,"breadcrumbs":3,"title":1},"70":{"body":31,"breadcrumbs":4,"title":2},"71":{"body":34,"breadcrumbs":4,"title":2},"72":{"body":182,"breadcrumbs":2,"title":1},"73":{"body":83,"breadcrumbs":2,"title":1},"74":{"body":0,"breadcrumbs":2,"title":1},"75":{"body":11,"breadcrumbs":4,"title":3},"76":{"body":27,"breadcrumbs":4,"title":3},"77":{"body":13,"breadcrumbs":4,"title":3},"78":{"body":41,"breadcrumbs":3,"title":2},"79":{"body":27,"breadcrumbs":2,"title":1},"8":{"body":24,"breadcrumbs":2,"title":1},"80":{"body":18,"breadcrumbs":6,"title":5},"81":{"body":19,"breadcrumbs":2,"title":1},"82":{"body":15,"breadcrumbs":3,"title":1},"83":{"body":26,"breadcrumbs":3,"title":1},"84":{"body":27,"breadcrumbs":4,"title":2},"85":{"body":30,"breadcrumbs":3,"title":1},"86":{"body":58,"breadcrumbs":5,"title":2},"87":{"body":16,"breadcrumbs":7,"title":2},"88":{"body":30,"breadcrumbs":9,"title":3},"89":{"body":32,"breadcrumbs":11,"title":4},"9":{"body":30,"breadcrumbs":2,"title":1},"90":{"body":63,"breadcrumbs":5,"title":1},"91":{"body":47,"breadcrumbs":7,"title":2},"92":{"body":46,"breadcrumbs":9,"title":3},"93":{"body":56,"breadcrumbs":7,"title":2},"94":{"body":51,"breadcrumbs":9,"title":3},"95":{"body":24,"breadcrumbs":5,"title":1},"96":{"body":50,"breadcrumbs":7,"title":2},"97":{"body":22,"breadcrumbs":9,"title":3},"98":{"body":22,"breadcrumbs":11,"title":4},"99":{"body":22,"breadcrumbs":9,"title":3}},"docs":{"0":{"body":"This book is intended to be an in-depth guide to canister development in TypeScript on the Internet Computer (IC). The first 19 chapters are an introductory guide into canister development with Azle . These chapters build on each other concept by concept, introducing the fundamentals required to create and deploy canisters to the IC. Chapter 20 is an in-depth reference of the APIs available to Azle canisters. Our intention is for new developers to use this book as a tutorial or course, starting at chapter 1 and working through chapter 19, using chapter 20 as a reference. There will also be a companion video series on YouTube. Each chapter here will begin with the video companion as soon as it is available. You should expect this book and its companion video series to continue to grow and change over time, as its authors and the IC grow and change. The Azle Book is subject to the following license and Azle's License Extension : MIT License Copyright (c) 2023 AZLE token holders (nlhft-2iaaa-aaaae-qaaua-cai) Permission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","breadcrumbs":"The Azle Book » The Azle Book","id":"0","title":"The Azle Book"},"1":{"body":"Azle is a TypeScript Canister Development Kit (CDK) for the Internet Computer (IC). In other words, it's a TypeScript/JavaScript runtime for building applications ( canisters ) on the IC. GitHub repo npm package Discord channel","breadcrumbs":"Azle (Beta) » Azle (Beta)","id":"1","title":"Azle (Beta)"},"10":{"body":"Run the following command to install dfx 0.14.2: DFX_VERSION=0.14.2 sh -ci \"$(curl -fsSL https://sdk.dfinity.org/install.sh)\" If after trying to run dfx commands you encounter an error such as dfx: command not found, you might need to add $HOME/bin to your path. Here's an example of doing this in your .bashrc: echo 'export PATH=\"$PATH:$HOME/bin\"' >> \"$HOME/.bashrc\"","breadcrumbs":"Installation » dfx","id":"10","title":"dfx"},"100":{"body":"This section is a work in progress. Examples: cycles import { ic, nat, $update } from 'azle'; // Moves all transferred cycles to the canister\n$update;\nexport function receiveCycles128(): nat { return ic.msgCyclesAccept128(ic.msgCyclesAvailable128() / 2n);\n}","breadcrumbs":"Reference » Call APIs » msg cycles available 128 » msg cycles available 128","id":"100","title":"msg cycles available 128"},"101":{"body":"This section is a work in progress. Examples: cycles import { ic, match, nat64, Result, $update } from 'azle';\nimport { cyclesCanister } from '../cycles'; // Reports the number of cycles returned from the Cycles canister\n$update;\nexport async function sendCycles(): Promise> { const result = await cyclesCanister .receiveCycles() .cycles(1_000_000n) .call(); return match(result, { Ok: () => ({ Ok: ic.msgCyclesRefunded() }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Call APIs » msg cycles refunded » msg cycles refunded","id":"101","title":"msg cycles refunded"},"102":{"body":"This section is a work in progress. Examples: cycles import { ic, match, nat, Result, $update } from 'azle';\nimport { cyclesCanister } from '../cycles'; // Reports the number of cycles returned from the Cycles canister\n$update;\nexport async function sendCycles128(): Promise> { const result = await cyclesCanister .receiveCycles128() .cycles128(1_000_000n) .call(); return match(result, { Ok: () => ({ Ok: ic.msgCyclesRefunded128() }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Call APIs » msg cycles refunded 128 » msg cycles refunded 128","id":"102","title":"msg cycles refunded 128"},"103":{"body":"This section is a work in progress. Examples: cross_canister_calls cycles import { NotifyResult, Principal, $update } from 'azle';\nimport { Canister2 } from '../canister2/types'; const canister2 = new Canister2( Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')\n); $update;\nexport function sendNotification(): NotifyResult { return canister2.receiveNotification('This is the notification').notify();\n}","breadcrumbs":"Reference » Call APIs » notify » notify","id":"103","title":"notify"},"104":{"body":"This section is a work in progress. Examples: notify_raw import { ic, NotifyResult, Principal, $update } from 'azle'; $update;\nexport function sendNotification(): NotifyResult { return ic.notifyRaw( Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai'), 'receiveNotification', Uint8Array.from(ic.candidEncode('()')), 0n );\n}","breadcrumbs":"Reference » Call APIs » notify raw » notify raw","id":"104","title":"notify raw"},"105":{"body":"This section is a work in progress. Examples: cycles import { NotifyResult, $update } from 'azle';\nimport { cyclesCanister } from '../cycles'; $update;\nexport function sendCycles128Notify(): NotifyResult { return cyclesCanister.receiveCycles128().cycles128(1_000_000n).notify();\n}","breadcrumbs":"Reference » Call APIs » notify with payment 128 » notify with payment 128","id":"105","title":"notify with payment 128"},"106":{"body":"This section is a work in progress. Examples: ic_api manual_reply rejections import { empty, ic, Manual, $query } from 'azle'; $query;\nexport function reject(message: string): Manual { ic.reject(message);\n}","breadcrumbs":"Reference » Call APIs » reject » reject","id":"106","title":"reject"},"107":{"body":"This section is a work in progress. Examples: rejections import { CallResult, ic, Principal, RejectionCode, Service, serviceUpdate, $update\n} from 'azle'; class Nonexistent extends Service { @serviceUpdate method: () => CallResult;\n} export const nonexistentCanister = new Nonexistent( Principal.fromText('rkp4c-7iaaa-aaaaa-aaaca-cai')\n); $update;\nexport async function getRejectionCodeDestinationInvalid(): Promise { await nonexistentCanister.method().call(); return ic.rejectCode();\n}","breadcrumbs":"Reference » Call APIs » reject code » reject code","id":"107","title":"reject code"},"108":{"body":"This section is a work in progress. Examples: rejections import { ic, $update } from 'azle';\nimport { someService } from '../some_service'; $update;\nexport async function getRejectionMessage(message: string): Promise { await someService.reject(message).call(); return ic.rejectMessage();\n}","breadcrumbs":"Reference » Call APIs » reject message » reject message","id":"108","title":"reject message"},"109":{"body":"This section is a work in progress. Examples: composite_queries manual_reply import { blob, ic, Manual, $update } from 'azle'; $update;\nexport function updateBlob(): Manual { ic.reply(new Uint8Array([83, 117, 114, 112, 114, 105, 115, 101, 33]));\n}","breadcrumbs":"Reference » Call APIs » reply » reply","id":"109","title":"reply"},"11":{"body":"Quick start Methodical start The project directory and file structure index.ts tsconfig.json dfx.json Local deployment Interacting with your canister from the command line Interacting with your canister from the web UI Let's build your first application (canister) with Azle! Before embarking please ensure you've followed all of the installation instructions . We'll build a simple Hello World canister that shows the basics of importing Azle, exposing a query method, exposing an update method, and storing some state in a global variable. We'll then interact with it from the command line and from our web browser.","breadcrumbs":"Hello World » Hello World","id":"11","title":"Hello World"},"110":{"body":"This section is a work in progress. Examples: manual_reply outgoing_http_requests import { blob, ic, int, Manual, Record, $update, Variant } from 'azle'; type Options = Variant<{ High: null; Medium: null; Low: null;\n}>; $update;\nexport function replyRaw(): Manual< Record<{ int: int; text: string; bool: boolean; blob: blob; variant: Options; }>\n> { ic.replyRaw( ic.candidEncode( '(record { \"int\" = 42; \"text\" = \"text\"; \"bool\" = true; \"blob\" = blob \"Surprise!\"; \"variant\" = variant { Medium } })' ) );\n}","breadcrumbs":"Reference » Call APIs » reply raw » reply raw","id":"110","title":"reply raw"},"111":{"body":"blob bool empty float32 float64 func int int8 int16 int32 int64 nat nat8 nat16 nat32 nat64 null opt principal record reserved service text variant vec","breadcrumbs":"Reference » Candid » Candid","id":"111","title":"Candid"},"112":{"body":"This section is a work in progress. The Azle type blob corresponds to the Candid type blob and will become a JavaScript Uint8Array at runtime. TypeScript: import { blob, $query } from 'azle'; $query;\nexport function getBlob(): blob { return Uint8Array.from([68, 73, 68, 76, 0, 0]);\n} $query;\nexport function printBlob(blob: blob): blob { console.log(typeof blob); return blob;\n} Candid: service : () -> { getBlob : () -> (vec nat8) query; printBlob : (vec nat8) -> (vec nat8) query;\n} dfx: dfx canister call candid_canister printBlob '(vec { 68; 73; 68; 76; 0; 0; })'\n(blob \"DIDL\\00\\00\") dfx canister call candid_canister printBlob '(blob \"DIDL\\00\\00\")'\n(blob \"DIDL\\00\\00\")","breadcrumbs":"Reference » Candid » blob » blob","id":"112","title":"blob"},"113":{"body":"This section is a work in progress. The TypeScript type boolean corresponds to the Candid type bool and will become a JavaScript Boolean at runtime. TypeScript: import { $query } from 'azle'; $query;\nexport function getBool(): boolean { return true;\n} $query;\nexport function printBool(bool: boolean): boolean { console.log(typeof bool); return bool;\n} Candid: service : () -> { getBool : () -> (bool) query; printBool : (bool) -> (bool) query;\n} dfx: dfx canister call candid_canister printBool '(true)'\n(true)","breadcrumbs":"Reference » Candid » bool » bool","id":"113","title":"bool"},"114":{"body":"This section is a work in progress. The Azle type empty corresponds to the Candid type empty and has no JavaScript value at runtime. TypeScript: import { empty, $query } from 'azle'; $query;\nexport function getEmpty(): empty { throw 'Anything you want';\n} // Note: It is impossible to call this function because it requires an argument\n// but there is no way to pass an \"empty\" value as an argument.\n$query;\nexport function printEmpty(empty: empty): empty { console.log(typeof empty); throw 'Anything you want';\n} Candid: service : () -> { getEmpty : () -> (empty) query; printEmpty : (empty) -> (empty) query;\n} dfx: dfx canister call candid_canister printEmpty '(\"You can put anything here\")'\nError: Failed to create argument blob.\nCaused by: Failed to create argument blob. Invalid data: Unable to serialize Candid values: type mismatch: \"You can put anything here\" cannot be of type empty","breadcrumbs":"Reference » Candid » empty » empty","id":"114","title":"empty"},"115":{"body":"This section is a work in progress. The Azle type float32 corresponds to the Candid type float32 and will become a JavaScript Number at runtime. TypeScript: import { float32, $query } from 'azle'; $query;\nexport function getFloat32(): float32 { return Math.PI;\n} $query;\nexport function printFloat32(float32: float32): float32 { console.log(typeof float32); return float32;\n} Candid: service : () -> { getFloat32 : () -> (float32) query; printFloat32 : (float32) -> (float32) query;\n} dfx: dfx canister call candid_canister printFloat32 '(3.1415927 : float32)'\n(3.1415927 : float32)","breadcrumbs":"Reference » Candid » float32 » float32","id":"115","title":"float32"},"116":{"body":"This section is a work in progress. The Azle type float64 and the TypeScript type number both correspond to the Candid type float64 and will become a JavaScript Number at runtime. TypeScript: import { float64, $query } from 'azle'; $query;\nexport function getFloat64(): float64 { return Math.E;\n} $query;\nexport function printFloat64(float64: float64): float64 { console.log(typeof float64); return float64;\n} Candid: service : () -> { getFloat64 : () -> (float64) query; printFloat64 : (float64) -> (float64) query;\n} dfx: dfx canister call candid_canister printFloat64 '(2.718281828459045 : float64)'\n(2.718281828459045 : float64)","breadcrumbs":"Reference » Candid » float64 » float64","id":"116","title":"float64"},"117":{"body":"This section is a work in progress. The Azle type Func corresponds to the Candid type func . It is a TypeScript Tuple and will become a JavaScript array with two elements at runtime. The first element is an @dfinity/principal and the second is a JavaScript string . The @dfinity/principal represents the principal of the canister/service where the function exists, and the string represents the function's name. A func acts as a callback, allowing the func receiver to know which canister instance and method must be used to call back. TypeScript: import { Func, Principal, $query, Query } from 'azle'; type BasicFunc = Func string>>; $query;\nexport function getBasicFunc(): BasicFunc { return [Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), 'getBasicFunc'];\n} $query;\nexport function printBasicFunc(basic_func: BasicFunc): BasicFunc { console.log(typeof basic_func); return basic_func;\n} Candid: service : () -> { getBasicFunc : () -> (func (text) -> (text) query) query; printBasicFunc : (func (text) -> (text) query) -> ( func (text) -> (text) query, ) query;\n} dfx: dfx canister call candid_canister printBasicFunc '(func \"r7inp-6aaaa-aaaaa-aaabq-cai\".getBasicFunc)'\n(func \"r7inp-6aaaa-aaaaa-aaabq-cai\".getBasicFunc)","breadcrumbs":"Reference » Candid » func » func","id":"117","title":"func"},"118":{"body":"This section is a work in progress. The Azle type int corresponds to the Candid type int and will become a JavaScript BigInt at runtime. TypeScript: import { int, $query } from 'azle'; $query;\nexport function getInt(): int { return 170_141_183_460_469_231_731_687_303_715_884_105_727n;\n} $query;\nexport function printInt(int: int): int { console.log(typeof int); return int;\n} Candid: service : () -> { getInt : () -> (int) query; printInt : (int) -> (int) query;\n} dfx: dfx canister call candid_canister printInt '(170_141_183_460_469_231_731_687_303_715_884_105_727 : int)'\n(170_141_183_460_469_231_731_687_303_715_884_105_727 : int)","breadcrumbs":"Reference » Candid » int » int","id":"118","title":"int"},"119":{"body":"This section is a work in progress. The Azle type int8 corresponds to the Candid type int8 and will become a JavaScript Number at runtime. TypeScript: import { int8, $query } from 'azle'; $query;\nexport function getInt8(): int8 { return 127;\n} $query;\nexport function printInt8(int8: int8): int8 { console.log(typeof int8); return int8;\n} Candid: service : () -> { getInt8 : () -> (int8) query; printInt8 : (int8) -> (int8) query;\n} dfx: dfx canister call candid_canister printInt8 '(127 : int8)'\n(127 : int8)","breadcrumbs":"Reference » Candid » int8 » int8","id":"119","title":"int8"},"12":{"body":"npx azle new hello_world\ncd hello_world npm install\nnpm run dfx_install\nnpm run replica_start\nnpm run canister_deploy_local npm run canister_call_set_message\nnpm run canister_call_get_message See the official azle_hello_world example for more information.","breadcrumbs":"Hello World » Quick Start","id":"12","title":"Quick Start"},"120":{"body":"This section is a work in progress. The Azle type int16 corresponds to the Candid type int16 and will become a JavaScript Number at runtime. TypeScript: import { int16, $query } from 'azle'; $query;\nexport function getInt16(): int16 { return 32_767;\n} $query;\nexport function printInt16(int16: int16): int16 { console.log(typeof int16); return int16;\n} Candid: service : () -> { getInt16 : () -> (int16) query; printInt16 : (int16) -> (int16) query;\n} dfx: dfx canister call candid_canister printInt16 '(32_767 : int16)'\n(32_767 : int16)","breadcrumbs":"Reference » Candid » int16 » int16","id":"120","title":"int16"},"121":{"body":"This section is a work in progress. The Azle type int32 corresponds to the Candid type int32 and will become a JavaScript Number at runtime. TypeScript: import { int32, $query } from 'azle'; $query;\nexport function getInt32(): int32 { return 2_147_483_647;\n} $query;\nexport function printInt32(int32: int32): int32 { console.log(typeof int32); return int32;\n} Candid: service : () -> { getInt32 : () -> (int32) query; printInt32 : (int32) -> (int32) query;\n} dfx: dfx canister call candid_canister printInt32 '(2_147_483_647 : int32)'\n(2_147_483_647 : int32)","breadcrumbs":"Reference » Candid » int32 » int32","id":"121","title":"int32"},"122":{"body":"This section is a work in progress. The Azle type int64 corresponds to the Candid type int64 and will become a JavaScript BigInt at runtime. TypeScript: import { int64, $query } from 'azle'; $query;\nexport function getInt64(): int64 { return 9_223_372_036_854_775_807n;\n} $query;\nexport function printInt64(int64: int64): int64 { console.log(typeof int64); return int64;\n} Candid: service : () -> { getInt64 : () -> (int64) query; printInt64 : (int64) -> (int64) query;\n} dfx: dfx canister call candid_canister printInt64 '(9_223_372_036_854_775_807 : int64)'\n(9_223_372_036_854_775_807 : int64)","breadcrumbs":"Reference » Candid » int64 » int64","id":"122","title":"int64"},"123":{"body":"This section is a work in progress. The Azle type nat corresponds to the Candid type nat and will become a JavaScript BigInt at runtime. TypeScript: import { nat, $query } from 'azle'; $query;\nexport function getNat(): nat { return 340_282_366_920_938_463_463_374_607_431_768_211_455n;\n} $query;\nexport function printNat(nat: nat): nat { console.log(typeof nat); return nat;\n} Candid: service : () -> { getNat : () -> (nat) query; printNat : (nat) -> (nat) query;\n} dfx: dfx canister call candid_canister printNat '(340_282_366_920_938_463_463_374_607_431_768_211_455 : nat)'\n(340_282_366_920_938_463_463_374_607_431_768_211_455 : nat)","breadcrumbs":"Reference » Candid » nat » nat","id":"123","title":"nat"},"124":{"body":"This section is a work in progress. The Azle type nat8 corresponds to the Candid type nat8 and will become a JavaScript Number at runtime. TypeScript: import { nat8, $query } from 'azle'; $query;\nexport function getNat8(): nat8 { return 255;\n} $query;\nexport function printNat8(nat8: nat8): nat8 { console.log(typeof nat8); return nat8;\n} Candid: service : () -> { getNat8 : () -> (nat8) query; printNat8 : (nat8) -> (nat8) query;\n} dfx: dfx canister call candid_canister printNat8 '(255 : nat8)'\n(255 : nat8)","breadcrumbs":"Reference » Candid » nat8 » nat8","id":"124","title":"nat8"},"125":{"body":"This section is a work in progress. The Azle type nat16 corresponds to the Candid type nat16 and will become a JavaScript Number at runtime. TypeScript: import { nat16, $query } from 'azle'; $query;\nexport function getNat16(): nat16 { return 65_535;\n} $query;\nexport function printNat16(nat16: nat16): nat16 { console.log(typeof nat16); return nat16;\n} Candid: service : () -> { getNat16 : () -> (nat16) query; printNat16 : (nat16) -> (nat16) query;\n} dfx: dfx canister call candid_canister printNat16 '(65_535 : nat16)'\n(65_535 : nat16)","breadcrumbs":"Reference » Candid » nat16 » nat16","id":"125","title":"nat16"},"126":{"body":"This section is a work in progress. The Azle type nat32 corresponds to the Candid type nat32 and will become a JavaScript Number at runtime. TypeScript: import { nat32, $query } from 'azle'; $query;\nexport function getNat32(): nat32 { return 4_294_967_295;\n} $query;\nexport function printNat32(nat32: nat32): nat32 { console.log(typeof nat32); return nat32;\n} Candid: service : () -> { getNat32 : () -> (nat32) query; printNat32 : (nat32) -> (nat32) query;\n} dfx: dfx canister call candid_canister printNat32 '(4_294_967_295 : nat32)'\n(4_294_967_295 : nat32)","breadcrumbs":"Reference » Candid » nat32 » nat32","id":"126","title":"nat32"},"127":{"body":"This section is a work in progress. The Azle type nat64 corresponds to the Candid type nat64 and will become a JavaScript BigInt at runtime. TypeScript: import { nat64, $query } from 'azle'; $query;\nexport function getNat64(): nat64 { return 18_446_744_073_709_551_615n;\n} $query;\nexport function printNat64(nat64: nat64): nat64 { console.log(typeof nat64); return nat64;\n} Candid: service : () -> { getNat64 : () -> (nat64) query; printNat64 : (nat64) -> (nat64) query;\n} dfx: dfx canister call candid_canister printNat64 '(18_446_744_073_709_551_615 : nat64)'\n(18_446_744_073_709_551_615 : nat64)","breadcrumbs":"Reference » Candid » nat64 » nat64","id":"127","title":"nat64"},"128":{"body":"This section is a work in progress. The TypeScript type null corresponds to the Candid type null and will become a JavaScript null at runtime. TypeScript: import { $query } from 'azle'; $query;\nexport function getNull(): null { return null;\n} $query;\nexport function printNull(null_: null): null { console.log(typeof null_); return null_;\n} Candid: service : () -> { getNull : () -> (null) query; printNull : (null) -> (null) query;\n} dfx: dfx canister call candid_canister printNull '(null)'\n(null : null)","breadcrumbs":"Reference » Candid » null » null","id":"128","title":"null"},"129":{"body":"This section is a work in progress. The Azle type Opt corresponds to the Candid type opt . It is a variant with Some and None cases. At runtime if the value of the variant is Some, the Some property of the variant object will have a value of the enclosed Opt type at runtime. TypeScript: import { Opt, $query } from 'azle'; $query;\nexport function getOptSome(): Opt { return Opt.Some(true);\n} $query;\nexport function getOptNone(): Opt { return Opt.None;\n} Candid: service : () -> { getOptNone : () -> (opt bool) query; getOptSome : () -> (opt bool) query;\n} dfx: dfx canister call candid_canister getOptSome\n(opt true) dfx canister call candid_canister getOptNone\n(null)","breadcrumbs":"Reference » Candid » opt » opt","id":"129","title":"opt"},"13":{"body":"","breadcrumbs":"Hello World » Methodical start","id":"13","title":"Methodical start"},"130":{"body":"This section is a work in progress. The Azle type Principal corresponds to the Candid type principal and will become an @dfinity/principal at runtime. TypeScript: import { Principal, $query } from 'azle'; $query;\nexport function getPrincipal(): Principal { return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai');\n} $query;\nexport function printPrincipal(principal: Principal): Principal { console.log(typeof principal); return principal;\n} Candid: service : () -> { getPrincipal : () -> (principal) query; printPrincipal : (principal) -> (principal) query;\n} dfx: dfx canister call candid_canister printPrincipal '(principal \"rrkah-fqaaa-aaaaa-aaaaq-cai\")'\n(principal \"rrkah-fqaaa-aaaaa-aaaaq-cai\")","breadcrumbs":"Reference » Candid » principal » principal","id":"130","title":"principal"},"131":{"body":"This section is a work in progress. TypeScript type aliases referring to object literals wrapped in the Record Azle type correspond to the Candid record type and will become JavaScript Objects at runtime. TypeScript: import { Principal, $query, Record } from 'azle'; type User = Record<{ id: Principal; username: string;\n}>; $query;\nexport function getUser(): User { return { id: Principal.fromUint8Array(Uint8Array.from([0])), username: 'lastmjs' };\n} $query;\nexport function printUser(user: User): User { console.log(typeof user); return user;\n} Candid: type User = record { id : principal; username : text };\nservice : () -> { getUser : () -> (User) query; printUser : (User) -> (User) query;\n} dfx: dfx canister call candid_canister printUser '(record { id = principal \"2ibo7-dia\"; username = \"lastmjs\" })'\n(record { id = principal \"2ibo7-dia\"; username = \"lastmjs\" })","breadcrumbs":"Reference » Candid » record » record","id":"131","title":"record"},"132":{"body":"This section is a work in progress. The Azle type reserved corresponds to the Candid type reserved , is the TypeScript type any, and will become a JavaScript null at runtime. TypeScript: import { $query, reserved } from 'azle'; $query;\nexport function getReserved(): reserved { return 'anything';\n} $query;\nexport function printReserved(reserved: reserved): reserved { console.log(typeof reserved); return reserved;\n} Candid: service : () -> { getReserved : () -> (reserved) query; printReserved : (reserved) -> (reserved) query;\n} dfx: dfx canister call candid_canister printReserved '(null)'\n(null : reserved)","breadcrumbs":"Reference » Candid » reserved » reserved","id":"132","title":"reserved"},"133":{"body":"This section is a work in progress. JavaScript classes that inherit from the Azle type Service correspond to the Candid service type and will become child classes capable of creating instances that can perform cross-canister calls at runtime. TypeScript: import { CallResult, Principal, $query, Result, Service, serviceQuery, serviceUpdate, $update\n} from 'azle'; class SomeService extends Service { @serviceQuery query1: () => CallResult; @serviceUpdate update1: () => CallResult;\n} $query;\nexport function getService(): SomeService { return new SomeService(Principal.fromText('aaaaa-aa'));\n} $update;\nexport async function callService( service: SomeService\n): Promise> { return await service.update1().call();\n} Candid: type ManualReply = variant { Ok : text; Err : text };\nservice : () -> { callService : ( service { query1 : () -> (bool) query; update1 : () -> (text) }, ) -> (ManualReply); getService : () -> ( service { query1 : () -> (bool) query; update1 : () -> (text) }, ) query;\n} dfx: dfx canister call candid_canister getService\n(service \"aaaaa-aa\")","breadcrumbs":"Reference » Candid » service » service","id":"133","title":"service"},"134":{"body":"This section is a work in progress. The TypeScript type string and the Azle type text both correspond to the Candid type text and will become a JavaScript String at runtime. TypeScript: import { $query } from 'azle'; $query;\nexport function getString(): string { return 'Hello world!';\n} $query;\nexport function printString(string: string): string { console.log(typeof string); return string;\n} Candid: service : () -> { getString : () -> (text) query; printString : (text) -> (text) query;\n} dfx: dfx canister call candid_canister printString '(\"Hello world!\")'\n(\"Hello world!\")","breadcrumbs":"Reference » Candid » text » text","id":"134","title":"text"},"135":{"body":"This section is a work in progress. TypeScript type aliases referring to object literals wrapped in the Variant Azle type correspond to the Candid variant type and will become JavaScript Objects at runtime. TypeScript: import { $query, Variant } from 'azle'; type Reaction = Variant<{ Fire: null; ThumbsUp: null; Emotion: Emotion;\n}>; type Emotion = Variant<{ Happy: null; Indifferent: null; Sad: null;\n}>; $query;\nexport function getReaction(): Reaction { return { Fire: null };\n} $query;\nexport function printReaction(reaction: Reaction): Reaction { console.log(typeof reaction); return reaction;\n} Candid: type Emotion = variant { Sad; Indifferent; Happy };\ntype Reaction = variant { Emotion : Emotion; Fire; ThumbsUp };\nservice : () -> { getReaction : () -> (Reaction) query; printReaction : (Reaction) -> (Reaction) query;\n} dfx: dfx canister call candid_canister printReaction '(variant { Fire })'\n(variant { Fire })","breadcrumbs":"Reference » Candid » variant » variant","id":"135","title":"variant"},"136":{"body":"This section is a work in progress. The Azle type Vec corresponds to the Candid type vec and will become a JavaScript array of the specified type at runtime (except for Vec which will become a Uint8Array, thus it is recommended to use the blob type instead of Vec). TypeScript: import { int32, $query, Vec } from 'azle'; $query;\nexport function getNumbers(): Vec { return [0, 1, 2, 3];\n} $query;\nexport function printNumbers(numbers: Vec): Vec { console.log(typeof numbers); return numbers;\n} Candid: service : () -> { getNumbers : () -> (vec int32) query; printNumbers : (vec int32) -> (vec int32) query;\n} dfx: dfx canister call candid_canister printNumbers '(vec { 0 : int32; 1 : int32; 2 : int32; 3 : int32 })'\n(vec { 0 : int32; 1 : int32; 2 : int32; 3 : int32 })","breadcrumbs":"Reference » Candid » vec » vec","id":"136","title":"vec"},"137":{"body":"candid decode candid encode canister balance canister balance 128 canister version canister id data certificate instruction counter is controller performance counter print set certified data time trap","breadcrumbs":"Reference » Canister APIs » Canister APIs","id":"137","title":"Canister APIs"},"138":{"body":"This section is a work in progress. Examples: call_raw candid_encoding import { blob, ic, $query } from 'azle'; // decodes Candid bytes to a Candid string\n$query;\nexport function candidDecode(candidEncoded: blob): string { return ic.candidDecode(candidEncoded);\n}","breadcrumbs":"Reference » Canister APIs » candid decode » candid decode","id":"138","title":"candid decode"},"139":{"body":"This section is a work in progress. Examples: call_raw candid_encoding manual_reply notify_raw outgoing_http_requests import { blob, ic, $query } from 'azle'; // encodes a Candid string to Candid bytes\n$query;\nexport function candidEncode(candidString: string): blob { return ic.candidEncode(candidString);\n}","breadcrumbs":"Reference » Canister APIs » candid encode » candid encode","id":"139","title":"candid encode"},"14":{"body":"Assuming you're starting completely from scratch, run these commands to setup your project's directory and file structure: mkdir azle_hello_world\ncd azle_hello_world mkdir src touch src/index.ts\ntouch tsconfig.json\ntouch dfx.json Now install Azle, which will create your package.json and package-lock.json files: npm install azle Open up azle_hello_world in your text editor (we recommend VS Code ).","breadcrumbs":"Hello World » The project directory and file structure","id":"14","title":"The project directory and file structure"},"140":{"body":"This section is a work in progress. Examples: cycles ic_api import { ic, nat64, $query } from 'azle'; // returns the amount of cycles available in the canister\n$query;\nexport function canisterBalance(): nat64 { return ic.canisterBalance();\n}","breadcrumbs":"Reference » Canister APIs » canister balance » canister balance","id":"140","title":"canister balance"},"141":{"body":"This section is a work in progress. Examples: cycles ic_api import { ic, nat, $query } from 'azle'; // returns the amount of cycles available in the canister\n$query;\nexport function canisterBalance128(): nat { return ic.canisterBalance128();\n}","breadcrumbs":"Reference » Canister APIs » canister balance 128 » canister balance 128","id":"141","title":"canister balance 128"},"142":{"body":"This section is a work in progress. Examples: ic_api import { ic, nat64, $query } from 'azle'; // returns the canister's version number\n$query;\nexport function canisterVersion(): nat64 { return ic.canisterVersion();\n}","breadcrumbs":"Reference » Canister APIs » canister version » canister version","id":"142","title":"canister version"},"143":{"body":"This section is a work in progress. Examples: ethereum_json_rpc ic_api http_counter outgoing_http_requests whoami import { ic, Principal, $query } from 'azle'; // returns this canister's id\n$query;\nexport function id(): Principal { return ic.id();\n}","breadcrumbs":"Reference » Canister APIs » canister id » canister id","id":"143","title":"canister id"},"144":{"body":"This section is a work in progress. Examples: ic_api import { blob, ic, Opt, $query } from 'azle'; // When called from a query call, returns the data certificate authenticating certified_data set by this canister. Returns None if not called from a query call.\n$query;\nexport function dataCertificate(): Opt { return ic.dataCertificate();\n}","breadcrumbs":"Reference » Canister APIs » data certificate » data certificate","id":"144","title":"data certificate"},"145":{"body":"This section is a work in progress. Examples: ic_api import { ic, nat64, $query } from 'azle'; // Returns the number of instructions that the canister executed since the last\n// entry point.\n$query;\nexport function instructionCounter(): nat64 { return ic.instructionCounter();\n}","breadcrumbs":"Reference » Canister APIs » instruction counter » instruction counter","id":"145","title":"instruction counter"},"146":{"body":"This section is a work in progress. Examples: ic_api import { ic, Principal, $query } from 'azle'; // determines whether the given principal is a controller of the canister\n$query;\nexport function isController(principal: Principal): boolean { return ic.isController(principal);\n}","breadcrumbs":"Reference » Canister APIs » is controller » is controller","id":"146","title":"is controller"},"147":{"body":"This section is a work in progress. Examples: ic_api import { ic, nat64, $query } from 'azle'; $query;\nexport function performanceCounter(): nat64 { return ic.performanceCounter(0);\n}","breadcrumbs":"Reference » Canister APIs » performance counter » performance counter","id":"147","title":"performance counter"},"148":{"body":"This section is a work in progress. Examples: ic_api null_example import { ic, $query } from 'azle'; // prints a message through the local replica's output\n$query;\nexport function print(message: string): boolean { ic.print(message); return true;\n}","breadcrumbs":"Reference » Canister APIs » print » print","id":"148","title":"print"},"149":{"body":"This section is a work in progress. Examples: ic_api import { blob, ic, $update } from 'azle'; // sets up to 32 bytes of certified data\n$update;\nexport function setCertifiedData(data: blob): void { ic.setCertifiedData(data);\n}","breadcrumbs":"Reference » Canister APIs » set certified data » set certified data","id":"149","title":"set certified data"},"15":{"body":"Here's the main code of the project, which you should put in the azle_hello_world/src/index.ts file of your canister: import { $query, $update } from 'azle'; // This is a global variable that is stored on the heap\nlet message: string = ''; // Query calls complete quickly because they do not go through consensus\n$query;\nexport function getMessage(): string { return message;\n} // Update calls take a few seconds to complete\n// This is because they persist state changes and go through consensus\n$update;\nexport function setMessage(newMessage: string): void { message = newMessage; // This change will be persisted\n} Let's discuss each section of the code. import { $query, $update } from 'azle'; The code starts off by importing the $query and $update annotations from azle. The azle module provides most of the Internet Computer (IC) APIs for your canister. // This is a global variable that is stored on the heap\nlet message: string = ''; We have created a global variable to store the state of our application. This variable is in scope to all of the functions defined in this module. We have annotated it with a type and set it equal to an empty string. // Query calls complete quickly because they do not go through consensus\n$query;\nexport function getMessage(): string { return message;\n} We are exposing a canister query method here. When query methods are called they execute quickly because they do not have to go through consensus. This method simply returns our global message variable. // Update calls take a few seconds to complete\n// This is because they persist state changes and go through consensus\n$update;\nexport function setMessage(newMessage: string): void { message = newMessage; // This change will be persisted\n} We are exposing an update method here. When update methods are called they take a few seconds to complete. This is because they persist changes and go through consensus. A majority of nodes in a subnet must agree on all state changes introduced in calls to update methods. This method accepts a string from the caller and will store it in our global message variable. That's it! We've created a very simple getter/setter Hello World application. But no Hello World project is complete without actually yelling Hello world! To do that, we'll need to setup the rest of our project.","breadcrumbs":"Hello World » index.ts","id":"15","title":"index.ts"},"150":{"body":"This section is a work in progress. Examples: audio_recorder ic_api import { ic, nat64, $query } from 'azle'; // returns the current timestamp\n$query;\nexport function time(): nat64 { return ic.time();\n}","breadcrumbs":"Reference » Canister APIs » time » time","id":"150","title":"time"},"151":{"body":"This section is a work in progress. Examples: cross_canister_calls ethereum_json_rpc http_counter ic_api outgoing_http_requests threshold_ecdsa import { ic, $query } from 'azle'; // traps with a message, stopping execution and discarding all state within the call\n$query;\nexport function trap(message: string): boolean { ic.trap(message); return true;\n}","breadcrumbs":"Reference » Canister APIs » trap » trap","id":"151","title":"trap"},"152":{"body":"heartbeat http_request http_request_update init inspect message post upgrade pre upgrade query update","breadcrumbs":"Reference » Canister Methods » Canister Methods","id":"152","title":"Canister Methods"},"153":{"body":"This section is a work in progress. Examples: heartbeat run_time_errors import { $heartbeat } from 'azle'; $heartbeat;\nexport function heartbeat(): void { console.log('this runs ~1 time per second');\n}","breadcrumbs":"Reference » Canister Methods » heartbeat » heartbeat","id":"153","title":"heartbeat"},"154":{"body":"This section is a work in progress. Examples: http_counter import { blob, Func, nat16, Opt, $query, Query, Record, Tuple, Variant, Vec\n} from 'azle'; type HttpRequest = Record<{ method: string; url: string; headers: Vec; body: blob;\n}>; type HttpResponse = Record<{ status_code: nat16; headers: Vec; body: blob; streaming_strategy: Opt; upgrade: Opt;\n}>; type Header = Tuple<[string, string]>; type StreamingStrategy = Variant<{ Callback: CallbackStrategy;\n}>; type CallbackStrategy = Record<{ callback: Callback; token: Token;\n}>; type Callback = Func StreamingCallbackHttpResponse>>; type StreamingCallbackHttpResponse = Record<{ body: blob; token: Opt;\n}>; type Token = Record<{ arbitrary_data: string;\n}>; $query;\nexport function http_request(req: HttpRequest): HttpResponse { return { status_code: 200, headers: [], body: Uint8Array.from([]), streaming_strategy: Opt.None, upgrade: Opt.Some(false) };\n}","breadcrumbs":"Reference » Canister Methods » http_request » http_request","id":"154","title":"http_request"},"155":{"body":"This section is a work in progress. Examples: http_counter import { blob, Func, nat16, Opt, Query, Record, Tuple, $update, Variant, Vec\n} from 'azle'; type HttpRequest = Record<{ method: string; url: string; headers: Vec; body: blob;\n}>; type HttpResponse = Record<{ status_code: nat16; headers: Vec; body: blob; streaming_strategy: Opt; upgrade: Opt;\n}>; type Header = Tuple<[string, string]>; type StreamingStrategy = Variant<{ Callback: CallbackStrategy;\n}>; type CallbackStrategy = Record<{ callback: Callback; token: Token;\n}>; type Callback = Func StreamingCallbackHttpResponse>>; type StreamingCallbackHttpResponse = Record<{ body: blob; token: Opt;\n}>; type Token = Record<{ arbitrary_data: string;\n}>; $update;\nexport function http_request_update(req: HttpRequest): HttpResponse { return { status_code: 200, headers: [], body: Uint8Array.from([]), streaming_strategy: Opt.None, upgrade: Opt.Some(false) };\n}","breadcrumbs":"Reference » Canister Methods » http_request_update » http_request_update","id":"155","title":"http_request_update"},"156":{"body":"This section is a work in progress. Examples: ethereum_json_rpc func_types init persistent-storage pre_and_post_upgrade whoami import { $init } from 'azle'; $init;\nexport function init(): void { console.log('This runs once when the canister is first initialized');\n}","breadcrumbs":"Reference » Canister Methods » init » init","id":"156","title":"init"},"157":{"body":"This section is a work in progress. Examples: inspect_message run_time_errors import { ic, $inspectMessage, $update } from 'azle'; $inspectMessage;\nexport function inspectMessage(): void { console.log('inspectMessage called'); if (ic.methodName() === 'accessible') { ic.acceptMessage(); return; } if (ic.methodName() === 'inaccessible') { return; } throw `Method \"${ic.methodName()}\" not allowed`;\n} $update;\nexport function accessible(): boolean { return true;\n} $update;\nexport function inaccessible(): boolean { return false;\n} $update;\nexport function alsoInaccessible(): boolean { return false;\n}","breadcrumbs":"Reference » Canister Methods » inspect message » inspect message","id":"157","title":"inspect message"},"158":{"body":"This section is a work in progress. Examples: pre_and_post_upgrade whoami import { $postUpgrade } from 'azle'; $postUpgrade;\nexport function postUpgrade(): void { console.log('This runs after every canister upgrade');\n}","breadcrumbs":"Reference » Canister Methods » post upgrade » post upgrade","id":"158","title":"post upgrade"},"159":{"body":"This section is a work in progress. Examples: pre_and_post_upgrade import { $preUpgrade } from 'azle'; $preUpgrade;\nexport function preUpgrade(): void { console.log('This runs before every canister upgrade');\n}","breadcrumbs":"Reference » Canister Methods » pre upgrade » pre upgrade","id":"159","title":"pre upgrade"},"16":{"body":"Create the following in azle_hello_world/tsconfig.json: { \"compilerOptions\": { \"strict\": true, \"target\": \"ES2020\", \"experimentalDecorators\": true, \"strictPropertyInitialization\": false, \"moduleResolution\": \"node\", \"allowJs\": true, \"outDir\": \"HACK_BECAUSE_OF_ALLOW_JS\" }\n}","breadcrumbs":"Hello World » tsconfig.json","id":"16","title":"tsconfig.json"},"160":{"body":"This section is a work in progress. import { $query } from 'azle'; $query;\nexport function simpleQuery(): string { return 'This is a query method';\n}","breadcrumbs":"Reference » Canister Methods » query » query","id":"160","title":"query"},"161":{"body":"This section is a work in progress. import { $query, $update } from 'azle'; let message = ''; $query;\nexport function getMessage(): string { return message;\n} $update;\nexport function setMessage(newMessage: string): void { message = newMessage;\n}","breadcrumbs":"Reference » Canister Methods » update » update","id":"161","title":"update"},"162":{"body":"You can provide environment variables to Azle canisters by specifying their names in your dfx.json file and then using the process.env object in Azle. Be aware that the environment variables that you specify in your dfx.json file will be included in plain text in your canister's Wasm binary.","breadcrumbs":"Reference » Environment Variables » Environment Variables","id":"162","title":"Environment Variables"},"163":{"body":"Modify your dfx.json file with the env property to specify which environment variables you would like included in your Azle canister's binary. In this case, CANISTER1_PRINCIPAL and CANISTER2_PRINCIPAL will be included: { \"canisters\": { \"canister1\": { \"type\": \"custom\", \"build\": \"npx azle canister1\", \"root\": \"canisters/canister1\", \"ts\": \"canisters/canister1/canister1.ts\", \"candid\": \"canisters/canister1/canister1.did\", \"wasm\": \".azle/canister1/canister1.wasm.gz\", \"declarations\": { \"output\": \"test/dfx_generated/canister1\", \"node_compatibility\": true }, \"env\": [\"CANISTER1_PRINCIPAL\", \"CANISTER2_PRINCIPAL\"] } }\n}","breadcrumbs":"Reference » Environment Variables » dfx.json","id":"163","title":"dfx.json"},"164":{"body":"You can access the specified environment variables in Azle like so: import { $query } from 'azle'; $query;\nexport function canister1PrincipalEnvVar(): string { return ( process.env.CANISTER1_PRINCIPAL ?? 'process.env.CANISTER1_PRINCIPAL is undefined' );\n} $query;\nexport function canister2PrincipalEnvVar(): string { return ( process.env.CANISTER2_PRINCIPAL ?? 'process.env.CANISTER2_PRINCIPAL is undefined' );\n}","breadcrumbs":"Reference » Environment Variables » process.env","id":"164","title":"process.env"},"165":{"body":"bitcoin_get_balance bitcoin_get_current_fee_percentiles bitcoin_get_utxos bitcoin_send_transaction canister_status create_canister delete_canister deposit_cycles ecdsa_public_key http_request install_code provisional_create_canister_with_cycles provisional_top_up_canister raw_rand sign_with_ecdsa start_canister stop_canister uninstall_code update_settings","breadcrumbs":"Reference » Management Canister » Management Canister","id":"165","title":"Management Canister"},"166":{"body":"This section is a work in progress. Examples: bitcoin import { Opt, Result, $update } from 'azle';\nimport { BitcoinNetwork, managementCanister, Satoshi\n} from 'azle/canisters/management'; const BITCOIN_API_CYCLE_COST = 100_000_000n; $update;\nexport async function getBalance( address: string\n): Promise> { return await managementCanister .bitcoin_get_balance({ address, min_confirmations: Opt.None, network: BitcoinNetwork.Regtest }) .cycles(BITCOIN_API_CYCLE_COST) .call();\n}","breadcrumbs":"Reference » Management Canister » bitcoin_get_balance » bitcoin_get_balance","id":"166","title":"bitcoin_get_balance"},"167":{"body":"This section is a work in progress. Examples: bitcoin import { Result, $update, Vec } from 'azle';\nimport { BitcoinNetwork, managementCanister, MillisatoshiPerByte\n} from 'azle/canisters/management'; const BITCOIN_API_CYCLE_COST = 100_000_000n; $update;\nexport async function getCurrentFeePercentiles(): Promise< Result, string>\n> { return await managementCanister .bitcoin_get_current_fee_percentiles({ network: BitcoinNetwork.Regtest }) .cycles(BITCOIN_API_CYCLE_COST) .call();\n}","breadcrumbs":"Reference » Management Canister » bitcoin_get_current_fee_percentiles » bitcoin_get_current_fee_percentiles","id":"167","title":"bitcoin_get_current_fee_percentiles"},"168":{"body":"This section is a work in progress. Examples: bitcoin import { Opt, Result, $update } from 'azle';\nimport { BitcoinNetwork, GetUtxosResult, managementCanister\n} from 'azle/canisters/management'; const BITCOIN_API_CYCLE_COST = 100_000_000n; $update;\nexport async function getUtxos( address: string\n): Promise> { return await managementCanister .bitcoin_get_utxos({ address, filter: Opt.None, network: BitcoinNetwork.Regtest }) .cycles(BITCOIN_API_CYCLE_COST) .call();\n}","breadcrumbs":"Reference » Management Canister » bitcoin_get_utxos » bitcoin_get_utxos","id":"168","title":"bitcoin_get_utxos"},"169":{"body":"This section is a work in progress. Examples: import { blob, match, Result, $update } from 'azle';\nimport { BitcoinNetwork, managementCanister } from 'azle/canisters/management'; const BITCOIN_BASE_TRANSACTION_COST = 5_000_000_000n;\nconst BITCOIN_CYCLE_COST_PER_TRANSACTION_BYTE = 20_000_000n; $update;\nexport async function sendTransaction( transaction: blob\n): Promise> { const transactionFee = BITCOIN_BASE_TRANSACTION_COST + BigInt(transaction.length) * BITCOIN_CYCLE_COST_PER_TRANSACTION_BYTE; const callResult = await managementCanister .bitcoin_send_transaction({ transaction, network: BitcoinNetwork.Regtest }) .cycles(transactionFee) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » bitcoin_send_transaction » bitcoin_send_transaction","id":"169","title":"bitcoin_send_transaction"},"17":{"body":"Create the following in azle_hello_world/dfx.json: { \"canisters\": { \"azle_hello_world\": { \"type\": \"custom\", \"build\": \"npx azle azle_hello_world\", \"root\": \"src\", \"ts\": \"src/index.ts\", \"candid\": \"src/index.did\", \"wasm\": \".azle/azle_hello_world/azle_hello_world.wasm.gz\" } }\n}","breadcrumbs":"Hello World » dfx.json","id":"17","title":"dfx.json"},"170":{"body":"This section is a work in progress. Examples: management_canister import { match, Result, $update } from 'azle';\nimport { CanisterStatusArgs, CanisterStatusResult, managementCanister\n} from 'azle/canisters/management'; $update;\nexport async function getCanisterStatus( args: CanisterStatusArgs\n): Promise> { const canisterStatusResultCallResult = await managementCanister .canister_status({ canister_id: args.canister_id }) .call(); return match(canisterStatusResultCallResult, { Ok: (canisterStatusResult) => ({ Ok: canisterStatusResult }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » canister_status » canister_status","id":"170","title":"canister_status"},"171":{"body":"This section is a work in progress. Examples: management_canister import { match, Opt, Result, $update } from 'azle';\nimport { CreateCanisterResult, managementCanister\n} from 'azle/canisters/management'; $update;\nexport async function executeCreateCanister(): Promise< Result\n> { const createCanisterResultCallResult = await managementCanister .create_canister({ settings: Opt.None }) .cycles(50_000_000_000_000n) .call(); return match(createCanisterResultCallResult, { Ok: (createCanisterResult) => ({ Ok: createCanisterResult }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » create_canister » create_canister","id":"171","title":"create_canister"},"172":{"body":"This section is a work in progress. Examples: management_canister import { match, Principal, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function executeDeleteCanister( canisterId: Principal\n): Promise> { const callResult = await managementCanister .delete_canister({ canister_id: canisterId }) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » delete_canister » delete_canister","id":"172","title":"delete_canister"},"173":{"body":"This section is a work in progress. Examples: management_canister import { match, Principal, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function executeDepositCycles( canisterId: Principal\n): Promise> { const callResult = await managementCanister .deposit_cycles({ canister_id: canisterId }) .cycles(1_000_000n) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » deposit_cycles » deposit_cycles","id":"173","title":"deposit_cycles"},"174":{"body":"This section is a work in progress. Examples: threshold_ecdsa import { blob, ic, match, Opt, Record, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function publicKey(): Promise< Result, string>\n> { const caller = ic.caller().toUint8Array(); const publicKeyResult = await managementCanister .ecdsa_public_key({ canister_id: Opt.None, derivation_path: [caller], key_id: { curve: { secp256k1: null }, name: 'dfx_test_key' } }) .call(); return match(publicKeyResult, { Ok: (ecdsaPublicKeyResult) => ({ Ok: { publicKey: ecdsaPublicKeyResult.public_key } }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » ecdsa_public_key » ecdsa_public_key","id":"174","title":"ecdsa_public_key"},"175":{"body":"This section is a work in progress. Examples: ethereum_json_rpc outgoing_http_requests import { ic, match, Opt, $query, $update } from 'azle';\nimport { HttpResponse, HttpTransformArgs, managementCanister\n} from 'azle/canisters/management'; $update;\nexport async function xkcd(): Promise { const httpResult = await managementCanister .http_request({ url: `https://xkcd.com/642/info.0.json`, max_response_bytes: Opt.Some(2_000n), method: { get: null }, headers: [], body: Opt.None, transform: Opt.Some({ function: [ic.id(), 'xkcdTransform'], context: Uint8Array.from([]) }) }) .cycles(50_000_000n) .call(); return match(httpResult, { Ok: (httpResponse) => httpResponse, Err: (err) => ic.trap(err) });\n} $query;\nexport function xkcdTransform(args: HttpTransformArgs): HttpResponse { return { ...args.response, headers: [] };\n}","breadcrumbs":"Reference » Management Canister » http_request » http_request","id":"175","title":"http_request"},"176":{"body":"This section is a work in progress. Examples: management_canister import { blob, match, Principal, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function executeInstallCode( canisterId: Principal, wasmModule: blob\n): Promise> { const callResult = await managementCanister .install_code({ mode: { install: null }, canister_id: canisterId, wasm_module: wasmModule, arg: Uint8Array.from([]) }) .cycles(100_000_000_000n) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » install_code » install_code","id":"176","title":"install_code"},"177":{"body":"This section is a work in progress. Examples: management_canister import { match, Opt, $update, Variant } from 'azle';\nimport { CreateCanisterResult, managementCanister\n} from 'azle/canisters/management'; $update;\nexport async function provisionalCreateCanisterWithCycles(): Promise< Variant<{ Ok: CreateCanisterResult; Err: string; }>\n> { const callResult = await managementCanister .provisional_create_canister_with_cycles({ amount: Opt.None, settings: Opt.None }) .call(); return match(callResult, { Ok: (provisionalCreateCanisterWithCyclesResult) => ({ Ok: provisionalCreateCanisterWithCyclesResult }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » provisional_create_canister_with_cycles » provisional_create_canister_with_cycles","id":"177","title":"provisional_create_canister_with_cycles"},"178":{"body":"This section is a work in progress. Examples: management_canister import { match, nat, Principal, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function provisionalTopUpCanister( canisterId: Principal, amount: nat\n): Promise> { const callResult = await managementCanister .provisional_top_up_canister({ canister_id: canisterId, amount }) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » provisional_top_up_canister » provisional_top_up_canister","id":"178","title":"provisional_top_up_canister"},"179":{"body":"This section is a work in progress. Examples: async/await heartbeat management_canister timers import { blob, match, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function getRandomness(): Promise { const randomnessResult = await managementCanister.raw_rand().call(); return match(randomnessResult, { Ok: (randomness) => randomness, Err: () => Uint8Array.from([]) });\n}","breadcrumbs":"Reference » Management Canister » raw_rand » raw_rand","id":"179","title":"raw_rand"},"18":{"body":"Let's deploy to our local replica. First startup the replica: dfx start --background Then deploy the canister: dfx deploy","breadcrumbs":"Hello World » Local deployment","id":"18","title":"Local deployment"},"180":{"body":"This section is a work in progress. Examples: threshold_ecdsa import { blob, ic, match, Record, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function sign( messageHash: blob\n): Promise, string>> { if (messageHash.length !== 32) { ic.trap('messageHash must be 32 bytes'); } const caller = ic.caller().toUint8Array(); const signatureResult = await managementCanister .sign_with_ecdsa({ message_hash: messageHash, derivation_path: [caller], key_id: { curve: { secp256k1: null }, name: 'dfx_test_key' } }) .cycles(10_000_000_000n) .call(); return match(signatureResult, { Ok: (signWithEcdsaResult) => ({ Ok: { signature: signWithEcdsaResult.signature } }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » sign_with_ecdsa » sign_with_ecdsa","id":"180","title":"sign_with_ecdsa"},"181":{"body":"This section is a work in progress. Examples: management_canister import { match, Principal, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function executeStartCanister( canisterId: Principal\n): Promise> { const callResult = await managementCanister .start_canister({ canister_id: canisterId }) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » start_canister » start_canister","id":"181","title":"start_canister"},"182":{"body":"This section is a work in progress. Examples: management_canister import { match, Principal, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function executeStopCanister( canisterId: Principal\n): Promise> { const callResult = await managementCanister .stop_canister({ canister_id: canisterId }) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » stop_canister » stop_canister","id":"182","title":"stop_canister"},"183":{"body":"This section is a work in progress. Examples: management_canister import { match, Principal, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function executeUninstallCode( canisterId: Principal\n): Promise> { const callResult = await managementCanister .uninstall_code({ canister_id: canisterId }) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » uninstall_code » uninstall_code","id":"183","title":"uninstall_code"},"184":{"body":"This section is a work in progress. Examples: management_canister import { match, Opt, Principal, Result, $update } from 'azle';\nimport { managementCanister } from 'azle/canisters/management'; $update;\nexport async function executeUpdateSettings( canisterId: Principal\n): Promise> { const callResult = await managementCanister .update_settings({ canister_id: canisterId, settings: { controllers: Opt.None, compute_allocation: Opt.Some(1n), memory_allocation: Opt.Some(3_000_000n), freezing_threshold: Opt.Some(2_000_000n) } }) .call(); return match(callResult, { Ok: () => ({ Ok: true }), Err: (err) => ({ Err: err }) });\n}","breadcrumbs":"Reference » Management Canister » update_settings » update_settings","id":"184","title":"update_settings"},"185":{"body":"Azle plugins allow developers to wrap Rust code in TypeScript/JavaScript APIs that can then be exposed to Azle canisters, providing a clean and simple developer experience with the underlying Rust code. Plugins are in a very early alpha state. You can create and use them now, but be aware that the API will be changing significantly in the near future. You can use the following example plugins as you create your own plugins:","breadcrumbs":"Reference » Plugins » Plugins","id":"185","title":"Plugins"},"186":{"body":"If you just want to create a plugin in the same repo as your project, see the plugins example .","breadcrumbs":"Reference » Plugins » Local plugin","id":"186","title":"Local plugin"},"187":{"body":"If you want to create a plugin that can be published and/or used with npm, see the ic-sqlite-plugin example .","breadcrumbs":"Reference » Plugins » npm plugin","id":"187","title":"npm plugin"},"188":{"body":"stable structures stable bytes stable grow stable read stable size stable write stable64 grow stable64 read stable64 size stable64 write","breadcrumbs":"Reference » Stable Memory » Stable Memory","id":"188","title":"Stable Memory"},"189":{"body":"This section is a work in progress. Examples: audio_recorder ethereum_json_rpc func_types http_counter inline_types persistent-storage pre_and_post_upgrade stable_structures import { Alias, nat64, nat8, Opt, $query, StableBTreeMap, Tuple, $update, Vec\n} from 'azle'; type Key = Alias;\ntype Value = Alias; let map = new StableBTreeMap(0, 100, 1_000); $query;\nexport function containsKey(key: Key): boolean { return map.containsKey(key);\n} $query;\nexport function get(key: Key): Opt { return map.get(key);\n} $update;\nexport function insert(key: Key, value: Value): Opt { return map.insert(key, value);\n} $query;\nexport function isEmpty(): boolean { return map.isEmpty();\n} $query;\nexport function items(): Vec> { return map.items();\n} $query;\nexport function keys(): Vec { return map.keys();\n} $query;\nexport function len(): nat64 { return map.len();\n} $update;\nexport function remove(key: Key): Opt { return map.remove(key);\n} $query;\nexport function values(): Vec { return map.values();\n}","breadcrumbs":"Reference » Stable Memory » stable structures » stable structures","id":"189","title":"stable structures"},"19":{"body":"Once we've deployed we can ask for our message: dfx canister call azle_hello_world getMessage We should see (\"\") representing an empty message. Now let's yell Hello World!: dfx canister call azle_hello_world setMessage '(\"Hello World!\")' Retrieve the message: dfx canister call azle_hello_world getMessage We should see (\"Hello World!\").","breadcrumbs":"Hello World » Interacting with your canister from the command line","id":"19","title":"Interacting with your canister from the command line"},"190":{"body":"This section is a work in progress. Examples: stable_memory import { blob, ic, $query } from 'azle'; $query;\nexport function stableBytes(): blob { return ic.stableBytes();\n}","breadcrumbs":"Reference » Stable Memory » stable bytes » stable bytes","id":"190","title":"stable bytes"},"191":{"body":"This section is a work in progress. Examples: stable_memory import { ic, nat32, StableGrowResult, $update } from 'azle'; $update;\nexport function stableGrow(newPages: nat32): StableGrowResult { return ic.stableGrow(newPages);\n}","breadcrumbs":"Reference » Stable Memory » stable grow » stable grow","id":"191","title":"stable grow"},"192":{"body":"This section is a work in progress. Examples: stable_memory import { blob, ic, nat32, $query } from 'azle'; $query;\nexport function stableRead(offset: nat32, length: nat32): blob { return ic.stableRead(offset, length);\n}","breadcrumbs":"Reference » Stable Memory » stable read » stable read","id":"192","title":"stable read"},"193":{"body":"This section is a work in progress. Examples: stable_memory import { ic, nat32, $query } from 'azle'; $query;\nexport function stableSize(): nat32 { return ic.stableSize();\n}","breadcrumbs":"Reference » Stable Memory » stable size » stable size","id":"193","title":"stable size"},"194":{"body":"This section is a work in progress. Examples: stable_memory import { blob, ic, nat32, $update } from 'azle'; $update;\nexport function stableWrite(offset: nat32, buf: blob): void { ic.stableWrite(offset, buf);\n}","breadcrumbs":"Reference » Stable Memory » stable write » stable write","id":"194","title":"stable write"},"195":{"body":"This section is a work in progress. Examples: stable_memory import { ic, nat64, Stable64GrowResult, $update } from 'azle'; $update;\nexport function stable64Grow(newPages: nat64): Stable64GrowResult { return ic.stable64Grow(newPages);\n}","breadcrumbs":"Reference » Stable Memory » stable64 grow » stable64 grow","id":"195","title":"stable64 grow"},"196":{"body":"This section is a work in progress. Examples: stable_memory import { blob, ic, nat64, $query } from 'azle'; $query;\nexport function stable64Read(offset: nat64, length: nat64): blob { return ic.stable64Read(offset, length);\n}","breadcrumbs":"Reference » Stable Memory » stable64 read » stable64 read","id":"196","title":"stable64 read"},"197":{"body":"This section is a work in progress. Examples: stable_memory import { ic, nat64, $query } from 'azle'; $query;\nexport function stable64Size(): nat64 { return ic.stable64Size();\n}","breadcrumbs":"Reference » Stable Memory » stable64 size » stable64 size","id":"197","title":"stable64 size"},"198":{"body":"This section is a work in progress. Examples: stable_memory import { blob, ic, nat64, $update } from 'azle'; $update;\nexport function stable64Write(offset: nat64, buf: blob): void { ic.stable64Write(offset, buf);\n}","breadcrumbs":"Reference » Stable Memory » stable64 write » stable64 write","id":"198","title":"stable64 write"},"199":{"body":"clear timer set timer set timer interval","breadcrumbs":"Reference » Timers » Timers","id":"199","title":"Timers"},"2":{"body":"Azle may have unknown security vulnerabilities due to the following: Azle does not yet have many live, successful, continuously operating applications deployed to the IC Azle does not yet have extensive automated property tests Azle does not yet have multiple independent security reviews/audits Azle heavily relies on Boa which is self-proclaimed to be experimental","breadcrumbs":"Azle (Beta) » Disclaimer","id":"2","title":"Disclaimer"},"20":{"body":"After deploying your canister, you should see output similar to the following in your terminal: Deployed canisters.\nURLs: Backend canister via Candid interface: azle_hello_world: http://127.0.0.1:8000/?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai&id=rrkah-fqaaa-aaaaa-aaaaq-cai Open up http://127.0.0.1:8000/?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai&id=rrkah-fqaaa-aaaaa-aaaaq-cai or the equivalent URL from your terminal to access the web UI and interact with your canister.","breadcrumbs":"Hello World » Interacting with your canister from the web UI","id":"20","title":"Interacting with your canister from the web UI"},"200":{"body":"This section is a work in progress. Examples: timers import { ic, TimerId, $update } from 'azle'; $update;\nexport function clearTimer(timerId: TimerId): void { ic.clearTimer(timerId);\n}","breadcrumbs":"Reference » Timers » clear timer » clear timer","id":"200","title":"clear timer"},"201":{"body":"This section is a work in progress. Examples: timers import { Duration, ic, TimerId, Tuple, $update } from 'azle'; $update;\nexport function setTimers(delay: Duration): Tuple<[TimerId, TimerId]> { const functionTimerId = ic.setTimer(delay, callback); const capturedValue = '🚩'; const closureTimerId = ic.setTimer(delay, () => { console.log(`closure called and captured value ${capturedValue}`); }); return [functionTimerId, closureTimerId];\n} function callback(): void { console.log('callback called');\n}","breadcrumbs":"Reference » Timers » set timer » set timer","id":"201","title":"set timer"},"202":{"body":"This section is a work in progress. Examples: timers import { Duration, ic, TimerId, Tuple, $update } from 'azle'; $update;\nexport function setTimerIntervals( interval: Duration\n): Tuple<[TimerId, TimerId]> { const functionTimerId = ic.setTimerInterval(interval, callback); const capturedValue = '🚩'; const closureTimerId = ic.setTimerInterval(interval, () => { console.log(`closure called and captured value ${capturedValue}`); }); return [functionTimerId, closureTimerId];\n} function callback(): void { console.log('callback called');\n}","breadcrumbs":"Reference » Timers » set timer interval » set timer interval","id":"202","title":"set timer interval"},"203":{"body":"The IC currently limits Wasm binaries to a relatively small size of ~2MiB (with some caveats). You are likely to hit this limit as your Azle canisters grow in size. Azle provides some automatic optimizations to help you deal with this limit. It is hoped that the IC-imposed limit will be greatly increased sometime in 2023. To optimize the Wasm binary of an Azle canister, you can add the opt_level property to your dfx.json with the following options: \"0\", \"1\", \"2\", \"3\", or \"4\". \"0\" is the default option if opt_level is not specified. Each option is intended to reduce the size of your Wasm binary as the value increases. Each option is likely to take longer to compile than the previous option. It is recommended to start at \"1\" and increase only as necessary. Here's an example using opt_level \"1\": { \"canisters\": { \"hello_world\": { \"type\": \"custom\", \"build\": \"npx azle hello_world\", \"root\": \"src\", \"ts\": \"src/index.ts\", \"candid\": \"src/index.did\", \"wasm\": \".azle/hello_world/hello_world.wasm.gz\", \"opt_level\": \"1\" } }\n}","breadcrumbs":"Reference » Wasm Binary Optimization » Wasm Binary Optimization","id":"203","title":"Wasm Binary Optimization"},"21":{"body":"Starting the local replica Deploying to the local replica Interacting with your canister Deploying to mainnet Common deployment issues There are two main Internet Computer (IC) environments that you will generally interact with: the local replica and mainnet. When developing on your local machine, our recommended flow is to start up a local replica in your project's root directoy and then deploy to it for local testing.","breadcrumbs":"Deployment » Deployment","id":"21","title":"Deployment"},"22":{"body":"Open a terminal and navigate to your project's root directory: dfx start Alternatively you can start the local replica as a background process: dfx start --background If you want to stop a local replica running in the background: dfx stop If you ever see this error after dfx stop: Error: Failed to kill all processes. Remaining: 627221 626923 627260 Then try this: sudo kill -9 627221\nsudo kill -9 626923\nsudo kill -9 627260 If your replica starts behaving strangely, we recommend starting the replica clean, which will clean the dfx state of your project: dfx start --clean","breadcrumbs":"Deployment » Starting the local replica","id":"22","title":"Starting the local replica"},"23":{"body":"To deploy all canisters defined in your dfx.json: dfx deploy To deploy an individual canister: dfx deploy canister_name","breadcrumbs":"Deployment » Deploying to the local replica","id":"23","title":"Deploying to the local replica"},"24":{"body":"As a developer you can generally interact with your canister in three ways: dfx command line dfx web UI @dfinity/agent","breadcrumbs":"Deployment » Interacting with your canister","id":"24","title":"Interacting with your canister"},"25":{"body":"You can see a more complete reference here . The commands you are likely to use most frequently are: # assume a canister named my_canister # builds and deploys all canisters specified in dfx.json\ndfx deploy # builds all canisters specified in dfx.json\ndfx build # builds and deploys my_canister\ndfx deploy my_canister # builds my_canister\ndfx build my_canister # removes the Wasm binary and state of my_canister\ndfx uninstall-code my_canister # calls the methodName method on my_canister with a string argument\ndfx canister call my_canister methodName '(\"This is a Candid string argument\")'","breadcrumbs":"Deployment » dfx command line","id":"25","title":"dfx command line"},"26":{"body":"After deploying your canister, you should see output similar to the following in your terminal: Deployed canisters.\nURLs: Backend canister via Candid interface: my_canister: http://127.0.0.1:8000/?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai&id=rrkah-fqaaa-aaaaa-aaaaq-cai Open up http://127.0.0.1:8000/?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai&id=rrkah-fqaaa-aaaaa-aaaaq-cai to access the web UI.","breadcrumbs":"Deployment » dfx web UI","id":"26","title":"dfx web UI"},"27":{"body":"@dfinity/agent is the TypeScript/JavaScript client library for interacting with canisters on the IC. If you are building a client web application, this is probably what you'll want to use. There are other agents for other languages as well: Java Python Rust","breadcrumbs":"Deployment » @dfinity/agent","id":"27","title":"@dfinity/agent"},"28":{"body":"Assuming you are setup with cycles , then you are ready to deploy to mainnet. To deploy all canisters defined in your dfx.json: dfx deploy --network ic To deploy an individual canister: dfx deploy --network ic canister_name","breadcrumbs":"Deployment » Deploying to mainnet","id":"28","title":"Deploying to mainnet"},"29":{"body":"Azle has many example projects showing nearly all Azle APIs. They can be found in the examples directory of the Azle GitHub repository . We'll highlight a few of them and some others here: Query Update Primitive Types Stable Structures Cycles Cross Canister Calls Management Canister Outgoing HTTP Requests Incoming HTTP Requests Pre and Post Upgrade Timers Multisig Vault ICRC-1 IC Chainlink Data Feeds Bitcoin ckBTC","breadcrumbs":"Examples » Examples","id":"29","title":"Examples"},"3":{"body":"We hope to get to a production-ready 1.0 in 2024. The following are the major blockers to 1.0: QuickJS/SpiderMonkey integration for performance, security, and stability Broad npm package support Extensive automated property testing Multiple independent security reviews/audits","breadcrumbs":"Azle (Beta) » Roadmap","id":"3","title":"Roadmap"},"30":{"body":"The Internet Computer (IC) is a decentralized cloud platform. Actually, it is better thought of as a progressively decentralizing cloud platform. Its full vision is yet to be fulfilled. It aims to be owned and operated by many independent entities in many geographies and legal jurisdictions throughout the world. This is in opposition to most traditional cloud platforms today, which are generally owned and operated by one overarching legal entity. The IC is composed of computer hardware nodes running the IC protocol software. Each running IC protocol software process is known as a replica. Nodes are assigned into groups known as subnets. Each subnet attempts to maximize its decentralization of nodes according to factors such as data center location and node operator independence. The subnets vary in size. Generally speaking the larger the size of the subnet the more secure it will be. Subnets currently range in size from 13 to 40 nodes, with most subnets having 13 nodes. IC applications, known as canisters, are deployed to specific subnets. They are then accessible through Internet Protocol requests such as HTTP. Each subnet replicates all canisters across all of its replicas. A consensus protocol is run by the replicas to ensure Byzantine Fault Tolerance . View the IC Dashboard to explore all data centers, subnets, node operators, and many other aspects of the IC.","breadcrumbs":"Internet Computer Overview » Internet Computer Overview","id":"30","title":"Internet Computer Overview"},"31":{"body":"Canisters are Internet Computer (IC) applications. They are the encapsulation of your code and state, and are essentially Wasm modules. State can be stored on the 4 GiB heap or in a larger 64 GiB location called stable memory. You can store state on the heap using your language's native global variables. You can store state in stable memory using low-level APIs or special stable data structures that behave similarly to native language data structures. State changes must go through a process called consensus. The consensus process ensures that state changes are Byzantine Fault Tolerant . This process takes a few seconds to complete. Operations on canister state are exposed to users through canister methods. These methods can be invoked through HTTP requests. Query methods allow state to be read and are low-latency. Update methods allow state to be changed and are higher-latency. Update methods take a few seconds to complete because of the consensus process.","breadcrumbs":"Canisters Overview » Canisters Overview","id":"31","title":"Canisters Overview"},"32":{"body":"","breadcrumbs":"Query Methods » Query Methods","id":"32","title":"Query Methods"},"33":{"body":"Annotate functions with $query Read-only Executed on a single node No consensus Latency on the order of ~100 milliseconds 5 billion Wasm instruction limit 4 GiB heap limit ~32k queries per second per canister The most basic way to expose your canister's functionality publicly is through a query method. Here's an example of a simple query method: import { $query } from 'azle'; $query;\nexport function getString(): string { return 'This is a query method!';\n} getString can be called from the outside world through the IC's HTTP API. You'll usually invoke this API from the dfx command line, dfx web UI, or an agent . From the dfx command line you can call it like this: dfx canister call my_canister getString Query methods are read-only. They do not persist any state changes. Take a look at the following example: import { $query } from 'azle'; let db: { [key: string]: string;\n} = {}; $query;\nexport function set(key: string, value: string): void { db[key] = value;\n} Calling set will perform the operation of setting the key property on the db object to value, but after the call finishes that change will be discarded. This is because query methods are executed on a single node machine and do not go through consensus . This results in lower latencies, perhaps on the order of 100 milliseconds. There is a limit to how much computation can be done in a single call to a query method. The current query call limit is 5 billion Wasm instructions . Here's an example of a query method that runs the risk of reaching the limit: import { nat32, $query } from 'azle'; $query;\nexport function pyramid(levels: nat32): string { return new Array(levels).fill(0).reduce((acc, _, index) => { const asterisks = new Array(index + 1).fill('*').join(''); return `${acc}${asterisks}\\n`; }, '');\n} From the dfx command line you can call pyramid like this: dfx canister call my_canister pyramid '(600)' With an argument of 600, pyramid will fail with an error ...exceeded the instruction limit for single message execution. Keep in mind that each query method invocation has up to 4 GiB of heap available. In terms of query scalability, an individual canister likely has an upper bound of ~36k queries per second .","breadcrumbs":"Query Methods » TLDR","id":"33","title":"TLDR"},"34":{"body":"","breadcrumbs":"Update Methods » Update Methods","id":"34","title":"Update Methods"},"35":{"body":"Annotate functions with $update Read-write Executed on many nodes Consensus Latency ~2-5 seconds 20 billion Wasm instruction limit 4 GiB heap limit 48 GiB stable memory limit ~900 updates per second per canister Update methods are similar to query methods, but state changes can be persisted. Here's an example of a simple update method: import { nat64, $update } from 'azle'; let counter = 0n; $update;\nexport function increment(): nat64 { return counter++;\n} Calling increment will return the current value of counter and then increase its value by 1. Because counter is a global variable, the change will be persisted to the heap, and subsequent query and update calls will have access to the new counter value. Because the Internet Computer (IC) persists changes with certain fault tolerance guarantees, update calls are executed on many nodes and go through consensus . This leads to latencies of ~2-5 seconds per update call. Due to the latency and other expenses involved with update methods, it is best to use them only when necessary. Look at the following example: import { $query, $update } from 'azle'; let message = ''; $query;\nexport function getMessage(): string { return message;\n} $update;\nexport function setMessage(newMessage: string): void { message = newMessage;\n} You'll notice that we use an update method, setMessage, only to perform the change to the global message variable. We use getMessage, a query method, to read the message. Keep in mind that the heap is limited to 4 GiB, and thus there is an upper bound to global variable storage capacity. You can imagine how a simple database like the following would eventually run out of memory with too many entries: import { Opt, $query, $update } from 'azle'; type Db = { [key: string]: string;\n}; let db: Db = {}; $query;\nexport function get(key: string): Opt { const value = db[key]; return value !== undefined ? Opt.Some(value) : Opt.None;\n} $update;\nexport function set(key: string, value: string): void { db[key] = value;\n} If you need more than 4 GiB of storage, consider taking advantage of the 48 GiB of stable memory. Stable structures like StableBTreeMap give you a nice API for interacting with stable memory. These data structures will be covered in more detail later . Here's a simple example: import { Opt, $query, StableBTreeMap, $update } from 'azle'; let db = new StableBTreeMap(0, 10, 10); $query;\nexport function get(key: string): Opt { return db.get(key);\n} $update;\nexport function set(key: string, value: string): void { db.insert(key, value);\n} So far we have only seen how state changes can be persisted. State changes can also be discarded by implicit or explicit traps. A trap is an immediate stop to execution with the ability to provide a message to the execution environment. Traps can be useful for ensuring that multiple operations are either all completed or all disregarded, or in other words atomic. Keep in mind that these guarantees do not hold once cross-canister calls are introduced, but that's a more advanced topic covered later . Here's an example of how to trap and ensure atomic changes to your database: import { ic, Opt, $query, Record, StableBTreeMap, $update, Vec } from 'azle'; type Entry = Record<{ key: string; value: string;\n}>; let db = new StableBTreeMap(0, 10, 10); $query;\nexport function get(key: string): Opt { return db.get(key);\n} $update;\nexport function set(key: string, value: string): void { db.insert(key, value);\n} $update;\nexport function setMany(entries: Vec): void { entries.forEach((entry) => { if (entry.key === 'trap') { ic.trap('explicit trap'); } db.insert(entry.key, entry.value); });\n} In addition to ic.trap, an explicit JavaScript throw or any unhandled exception will also trap. There is a limit to how much computation can be done in a single call to an update method. The current update call limit is 20 billion Wasm instructions . If we modify our database example, we can introduce an update method that runs the risk of reaching the limit: import { nat64, Opt, $query, StableBTreeMap, $update } from 'azle'; let db = new StableBTreeMap(0, 1_000, 1_000); $query;\nexport function get(key: string): Opt { return db.get(key);\n} $update;\nexport function set(key: string, value: string): void { db.insert(key, value);\n} $update;\nexport function setMany(numEntries: nat64): void { for (let i = 0; i < numEntries; i++) { db.insert(i.toString(), i.toString()); }\n} From the dfx command line you can call setMany like this: dfx canister call my_canister setMany '(100_000)' With an argument of 100_000, setMany will fail with an error ...exceeded the instruction limit for single message execution. In terms of update scalability, an individual canister likely has an upper bound of ~900 updates per second .","breadcrumbs":"Update Methods » TLDR","id":"35","title":"TLDR"},"36":{"body":"text blob nat nat64 nat32 nat16 nat8 int int64 int32 int16 int8 float64 float32 bool null vec opt record variant func service principal reserved empty Candid is an interface description language created by DFINITY . It can be used to define interfaces between services (canisters), allowing canisters and clients written in various languages to easily interact with each other. Azle allows you to express Candid types through a combination of native and Azle-provided TypeScript types. These types will be necessary in various places as you define your canister. For example, Candid types must be used when defining the parameters and return types of your query and update methods. It's important to note that the Candid types are represented at runtime using specific JavaScript data structures that may differ in behavior from the description of the actual Candid type. For example, a float32 Candid type is a JavaScript Number , a nat64 is a JavaScript BigInt , and an int is also a JavaScript BigInt . Keep this in mind as it may result in unexpected behavior. Each Candid type and its equivalent JavaScript runtime value is explained in more detail in this chapter. A reference of all Candid types available on the Internet Computer (IC) can be found here . The following is a simple example showing how to import and use most of the Candid types available in Azle: import { blob, CallResult, float64, float32, Func, int, int8, int16, int32, int64, nat, nat8, nat16, nat32, nat64, Opt, Principal, $query, Query, Record, Service, serviceQuery, serviceUpdate, Variant, Vec\n} from 'azle'; type Candid = Record<{ text: string; blob: blob; nat: nat; nat64: nat64; nat32: nat32; nat16: nat16; nat8: nat8; int: int; int64: int64; int32: int32; int16: int16; int8: int8; float64: float64; float32: float32; bool: boolean; null: null; vec: Vec; opt: Opt; record: Record<{ firstName: string; lastName: string; age: nat8; }>; variant: Variant<{ Tag1: null; Tag2: null; Tag3: int; }>; func: Func Candid>>; service: MyService; principal: Principal;\n}>; class MyService extends Service { @serviceQuery query1: () => CallResult; @serviceUpdate update1: () => CallResult;\n} $query;\nexport function candidTypes(): Candid { return { text: 'text', blob: Uint8Array.from([]), nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n, nat64: 18_446_744_073_709_551_615n, nat32: 4_294_967_295, nat16: 65_535, nat8: 255, int: 170_141_183_460_469_231_731_687_303_715_884_105_727n, int64: 9_223_372_036_854_775_807n, int32: 2_147_483_647, int16: 32_767, int8: 127, float64: Math.E, float32: Math.PI, bool: true, null: null, vec: ['has one element'], opt: Opt.None, record: { firstName: 'John', lastName: 'Doe', age: 35 }, variant: { Tag1: null }, func: [ Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), 'candidTypes' ], service: new MyService(Principal.fromText('aaaaa-aa')), principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai') };\n} Calling candidTypes with dfx will return: ( record { \"int\" = 170_141_183_460_469_231_731_687_303_715_884_105_727 : int; \"nat\" = 340_282_366_920_938_463_463_374_607_431_768_211_455 : nat; \"opt\" = null; \"vec\" = vec { \"has one element\" }; \"service\" = service \"aaaaa-aa\"; \"principal\" = principal \"ryjl3-tyaaa-aaaaa-aaaba-cai\"; \"blob\" = vec {}; \"bool\" = true; \"func\" = func \"rrkah-fqaaa-aaaaa-aaaaq-cai\".candidTypes; \"int8\" = 127 : int8; \"nat8\" = 255 : nat8; \"null\" = null : null; \"text\" = \"text\"; \"nat16\" = 65_535 : nat16; \"nat32\" = 4_294_967_295 : nat32; \"nat64\" = 18_446_744_073_709_551_615 : nat64; \"int16\" = 32_767 : int16; \"int32\" = 2_147_483_647 : int32; \"int64\" = 9_223_372_036_854_775_807 : int64; \"variant\" = variant { Tag1 }; \"float32\" = 3.1415927 : float32; \"float64\" = 2.718281828459045 : float64; \"record\" = record { age = 35 : nat8; lastName = \"Doe\"; firstName = \"John\" }; },\n)","breadcrumbs":"Candid » Candid","id":"36","title":"Candid"},"37":{"body":"The TypeScript type string and the Azle type text both correspond to the Candid type text and will become a JavaScript String at runtime. TypeScript: import { $query } from 'azle'; $query;\nexport function getString(): string { return 'Hello world!';\n} $query;\nexport function printString(string: string): string { console.log(typeof string); return string;\n} Candid: service : () -> { getString : () -> (text) query; printString : (text) -> (text) query;\n} dfx: dfx canister call candid_canister printString '(\"Hello world!\")'\n(\"Hello world!\")","breadcrumbs":"Candid » text","id":"37","title":"text"},"38":{"body":"The Azle type blob corresponds to the Candid type blob and will become a JavaScript Uint8Array at runtime. TypeScript: import { blob, $query } from 'azle'; $query;\nexport function getBlob(): blob { return Uint8Array.from([68, 73, 68, 76, 0, 0]);\n} $query;\nexport function printBlob(blob: blob): blob { console.log(typeof blob); return blob;\n} Candid: service : () -> { getBlob : () -> (vec nat8) query; printBlob : (vec nat8) -> (vec nat8) query;\n} dfx: dfx canister call candid_canister printBlob '(vec { 68; 73; 68; 76; 0; 0; })'\n(blob \"DIDL\\00\\00\") dfx canister call candid_canister printBlob '(blob \"DIDL\\00\\00\")'\n(blob \"DIDL\\00\\00\")","breadcrumbs":"Candid » blob","id":"38","title":"blob"},"39":{"body":"The Azle type nat corresponds to the Candid type nat and will become a JavaScript BigInt at runtime. TypeScript: import { nat, $query } from 'azle'; $query;\nexport function getNat(): nat { return 340_282_366_920_938_463_463_374_607_431_768_211_455n;\n} $query;\nexport function printNat(nat: nat): nat { console.log(typeof nat); return nat;\n} Candid: service : () -> { getNat : () -> (nat) query; printNat : (nat) -> (nat) query;\n} dfx: dfx canister call candid_canister printNat '(340_282_366_920_938_463_463_374_607_431_768_211_455 : nat)'\n(340_282_366_920_938_463_463_374_607_431_768_211_455 : nat)","breadcrumbs":"Candid » nat","id":"39","title":"nat"},"4":{"body":"Azle is currently developed by Demergent Labs , a for-profit company with a grant from DFINITY . Demergent Labs' vision is to accelerate the adoption of Web3, the Internet Computer, and sustainable open source.","breadcrumbs":"Azle (Beta) » Demergent Labs","id":"4","title":"Demergent Labs"},"40":{"body":"The Azle type nat64 corresponds to the Candid type nat64 and will become a JavaScript BigInt at runtime. TypeScript: import { nat64, $query } from 'azle'; $query;\nexport function getNat64(): nat64 { return 18_446_744_073_709_551_615n;\n} $query;\nexport function printNat64(nat64: nat64): nat64 { console.log(typeof nat64); return nat64;\n} Candid: service : () -> { getNat64 : () -> (nat64) query; printNat64 : (nat64) -> (nat64) query;\n} dfx: dfx canister call candid_canister printNat64 '(18_446_744_073_709_551_615 : nat64)'\n(18_446_744_073_709_551_615 : nat64)","breadcrumbs":"Candid » nat64","id":"40","title":"nat64"},"41":{"body":"The Azle type nat32 corresponds to the Candid type nat32 and will become a JavaScript Number at runtime. TypeScript: import { nat32, $query } from 'azle'; $query;\nexport function getNat32(): nat32 { return 4_294_967_295;\n} $query;\nexport function printNat32(nat32: nat32): nat32 { console.log(typeof nat32); return nat32;\n} Candid: service : () -> { getNat32 : () -> (nat32) query; printNat32 : (nat32) -> (nat32) query;\n} dfx: dfx canister call candid_canister printNat32 '(4_294_967_295 : nat32)'\n(4_294_967_295 : nat32)","breadcrumbs":"Candid » nat32","id":"41","title":"nat32"},"42":{"body":"The Azle type nat16 corresponds to the Candid type nat16 and will become a JavaScript Number at runtime. TypeScript: import { nat16, $query } from 'azle'; $query;\nexport function getNat16(): nat16 { return 65_535;\n} $query;\nexport function printNat16(nat16: nat16): nat16 { console.log(typeof nat16); return nat16;\n} Candid: service : () -> { getNat16 : () -> (nat16) query; printNat16 : (nat16) -> (nat16) query;\n} dfx: dfx canister call candid_canister printNat16 '(65_535 : nat16)'\n(65_535 : nat16)","breadcrumbs":"Candid » nat16","id":"42","title":"nat16"},"43":{"body":"The Azle type nat8 corresponds to the Candid type nat8 and will become a JavaScript Number at runtime. TypeScript: import { nat8, $query } from 'azle'; $query;\nexport function getNat8(): nat8 { return 255;\n} $query;\nexport function printNat8(nat8: nat8): nat8 { console.log(typeof nat8); return nat8;\n} Candid: service : () -> { getNat8 : () -> (nat8) query; printNat8 : (nat8) -> (nat8) query;\n} dfx: dfx canister call candid_canister printNat8 '(255 : nat8)'\n(255 : nat8)","breadcrumbs":"Candid » nat8","id":"43","title":"nat8"},"44":{"body":"The Azle type int corresponds to the Candid type int and will become a JavaScript BigInt at runtime. TypeScript: import { int, $query } from 'azle'; $query;\nexport function getInt(): int { return 170_141_183_460_469_231_731_687_303_715_884_105_727n;\n} $query;\nexport function printInt(int: int): int { console.log(typeof int); return int;\n} Candid: service : () -> { getInt : () -> (int) query; printInt : (int) -> (int) query;\n} dfx: dfx canister call candid_canister printInt '(170_141_183_460_469_231_731_687_303_715_884_105_727 : int)'\n(170_141_183_460_469_231_731_687_303_715_884_105_727 : int)","breadcrumbs":"Candid » int","id":"44","title":"int"},"45":{"body":"The Azle type int64 corresponds to the Candid type int64 and will become a JavaScript BigInt at runtime. TypeScript: import { int64, $query } from 'azle'; $query;\nexport function getInt64(): int64 { return 9_223_372_036_854_775_807n;\n} $query;\nexport function printInt64(int64: int64): int64 { console.log(typeof int64); return int64;\n} Candid: service : () -> { getInt64 : () -> (int64) query; printInt64 : (int64) -> (int64) query;\n} dfx: dfx canister call candid_canister printInt64 '(9_223_372_036_854_775_807 : int64)'\n(9_223_372_036_854_775_807 : int64)","breadcrumbs":"Candid » int64","id":"45","title":"int64"},"46":{"body":"The Azle type int32 corresponds to the Candid type int32 and will become a JavaScript Number at runtime. TypeScript: import { int32, $query } from 'azle'; $query;\nexport function getInt32(): int32 { return 2_147_483_647;\n} $query;\nexport function printInt32(int32: int32): int32 { console.log(typeof int32); return int32;\n} Candid: service : () -> { getInt32 : () -> (int32) query; printInt32 : (int32) -> (int32) query;\n} dfx: dfx canister call candid_canister printInt32 '(2_147_483_647 : int32)'\n(2_147_483_647 : int32)","breadcrumbs":"Candid » int32","id":"46","title":"int32"},"47":{"body":"The Azle type int16 corresponds to the Candid type int16 and will become a JavaScript Number at runtime. TypeScript: import { int16, $query } from 'azle'; $query;\nexport function getInt16(): int16 { return 32_767;\n} $query;\nexport function printInt16(int16: int16): int16 { console.log(typeof int16); return int16;\n} Candid: service : () -> { getInt16 : () -> (int16) query; printInt16 : (int16) -> (int16) query;\n} dfx: dfx canister call candid_canister printInt16 '(32_767 : int16)'\n(32_767 : int16)","breadcrumbs":"Candid » int16","id":"47","title":"int16"},"48":{"body":"The Azle type int8 corresponds to the Candid type int8 and will become a JavaScript Number at runtime. TypeScript: import { int8, $query } from 'azle'; $query;\nexport function getInt8(): int8 { return 127;\n} $query;\nexport function printInt8(int8: int8): int8 { console.log(typeof int8); return int8;\n} Candid: service : () -> { getInt8 : () -> (int8) query; printInt8 : (int8) -> (int8) query;\n} dfx: dfx canister call candid_canister printInt8 '(127 : int8)'\n(127 : int8)","breadcrumbs":"Candid » int8","id":"48","title":"int8"},"49":{"body":"The Azle type float64 and the TypeScript type number both correspond to the Candid type float64 and will become a JavaScript Number at runtime. TypeScript: import { float64, $query } from 'azle'; $query;\nexport function getFloat64(): float64 { return Math.E;\n} $query;\nexport function printFloat64(float64: float64): float64 { console.log(typeof float64); return float64;\n} Candid: service : () -> { getFloat64 : () -> (float64) query; printFloat64 : (float64) -> (float64) query;\n} dfx: dfx canister call candid_canister printFloat64 '(2.718281828459045 : float64)'\n(2.718281828459045 : float64)","breadcrumbs":"Candid » float64","id":"49","title":"float64"},"5":{"body":"Azle and the IC provide unique benefits and drawbacks, and both are not currently suitable for all application use-cases. The following information will help you to determine when Azle and the IC might be beneficial for your use-case.","breadcrumbs":"Azle (Beta) » Benefits and drawbacks","id":"5","title":"Benefits and drawbacks"},"50":{"body":"The Azle type float32 corresponds to the Candid type float32 and will become a JavaScript Number at runtime. TypeScript: import { float32, $query } from 'azle'; $query;\nexport function getFloat32(): float32 { return Math.PI;\n} $query;\nexport function printFloat32(float32: float32): float32 { console.log(typeof float32); return float32;\n} Candid: service : () -> { getFloat32 : () -> (float32) query; printFloat32 : (float32) -> (float32) query;\n} dfx: dfx canister call candid_canister printFloat32 '(3.1415927 : float32)'\n(3.1415927 : float32)","breadcrumbs":"Candid » float32","id":"50","title":"float32"},"51":{"body":"The TypeScript type boolean corresponds to the Candid type bool and will become a JavaScript Boolean at runtime. TypeScript: import { $query } from 'azle'; $query;\nexport function getBool(): boolean { return true;\n} $query;\nexport function printBool(bool: boolean): boolean { console.log(typeof bool); return bool;\n} Candid: service : () -> { getBool : () -> (bool) query; printBool : (bool) -> (bool) query;\n} dfx: dfx canister call candid_canister printBool '(true)'\n(true)","breadcrumbs":"Candid » bool","id":"51","title":"bool"},"52":{"body":"The TypeScript type null corresponds to the Candid type null and will become a JavaScript null at runtime. TypeScript: import { $query } from 'azle'; $query;\nexport function getNull(): null { return null;\n} $query;\nexport function printNull(null_: null): null { console.log(typeof null_); return null_;\n} Candid: service : () -> { getNull : () -> (null) query; printNull : (null) -> (null) query;\n} dfx: dfx canister call candid_canister printNull '(null)'\n(null : null)","breadcrumbs":"Candid » null","id":"52","title":"null"},"53":{"body":"The Azle type Vec corresponds to the Candid type vec and will become a JavaScript array of the specified type at runtime (except for Vec which will become a Uint8Array, thus it is recommended to use the blob type instead of Vec). TypeScript: import { int32, $query, Vec } from 'azle'; $query;\nexport function getNumbers(): Vec { return [0, 1, 2, 3];\n} $query;\nexport function printNumbers(numbers: Vec): Vec { console.log(typeof numbers); return numbers;\n} Candid: service : () -> { getNumbers : () -> (vec int32) query; printNumbers : (vec int32) -> (vec int32) query;\n} dfx: dfx canister call candid_canister printNumbers '(vec { 0 : int32; 1 : int32; 2 : int32; 3 : int32 })'\n(vec { 0 : int32; 1 : int32; 2 : int32; 3 : int32 })","breadcrumbs":"Candid » vec","id":"53","title":"vec"},"54":{"body":"The Azle type Opt corresponds to the Candid type opt . It is a variant with Some and None cases. At runtime if the value of the variant is Some, the Some property of the variant object will have a value of the enclosed Opt type at runtime. TypeScript: import { Opt, $query } from 'azle'; $query;\nexport function getOptSome(): Opt { return Opt.Some(true);\n} $query;\nexport function getOptNone(): Opt { return Opt.None;\n} Candid: service : () -> { getOptNone : () -> (opt bool) query; getOptSome : () -> (opt bool) query;\n} dfx: dfx canister call candid_canister getOptSome\n(opt true) dfx canister call candid_canister getOptNone\n(null)","breadcrumbs":"Candid » opt","id":"54","title":"opt"},"55":{"body":"TypeScript type aliases referring to object literals wrapped in the Record Azle type correspond to the Candid record type and will become JavaScript Objects at runtime. TypeScript: import { Principal, $query, Record } from 'azle'; type User = Record<{ id: Principal; username: string;\n}>; $query;\nexport function getUser(): User { return { id: Principal.fromUint8Array(Uint8Array.from([0])), username: 'lastmjs' };\n} $query;\nexport function printUser(user: User): User { console.log(typeof user); return user;\n} Candid: type User = record { id : principal; username : text };\nservice : () -> { getUser : () -> (User) query; printUser : (User) -> (User) query;\n} dfx: dfx canister call candid_canister printUser '(record { id = principal \"2ibo7-dia\"; username = \"lastmjs\" })'\n(record { id = principal \"2ibo7-dia\"; username = \"lastmjs\" })","breadcrumbs":"Candid » record","id":"55","title":"record"},"56":{"body":"TypeScript type aliases referring to object literals wrapped in the Variant Azle type correspond to the Candid variant type and will become JavaScript Objects at runtime. TypeScript: import { $query, Variant } from 'azle'; type Reaction = Variant<{ Fire: null; ThumbsUp: null; Emotion: Emotion;\n}>; type Emotion = Variant<{ Happy: null; Indifferent: null; Sad: null;\n}>; $query;\nexport function getReaction(): Reaction { return { Fire: null };\n} $query;\nexport function printReaction(reaction: Reaction): Reaction { console.log(typeof reaction); return reaction;\n} Candid: type Emotion = variant { Sad; Indifferent; Happy };\ntype Reaction = variant { Emotion : Emotion; Fire; ThumbsUp };\nservice : () -> { getReaction : () -> (Reaction) query; printReaction : (Reaction) -> (Reaction) query;\n} dfx: dfx canister call candid_canister printReaction '(variant { Fire })'\n(variant { Fire })","breadcrumbs":"Candid » variant","id":"56","title":"variant"},"57":{"body":"The Azle type Func corresponds to the Candid type func . It is a TypeScript Tuple and will become a JavaScript array with two elements at runtime. The first element is an @dfinity/principal and the second is a JavaScript string . The @dfinity/principal represents the principal of the canister/service where the function exists, and the string represents the function's name. A func acts as a callback, allowing the func receiver to know which canister instance and method must be used to call back. TypeScript: import { Func, Principal, $query, Query } from 'azle'; type BasicFunc = Func string>>; $query;\nexport function getBasicFunc(): BasicFunc { return [Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), 'getBasicFunc'];\n} $query;\nexport function printBasicFunc(basicFunc: BasicFunc): BasicFunc { console.log(typeof basicFunc); return basicFunc;\n} Candid: service : () -> { getBasicFunc : () -> (func (text) -> (text) query) query; printBasicFunc : (func (text) -> (text) query) -> ( func (text) -> (text) query, ) query;\n} dfx: dfx canister call candid_canister printBasicFunc '(func \"r7inp-6aaaa-aaaaa-aaabq-cai\".getBasicFunc)'\n(func \"r7inp-6aaaa-aaaaa-aaabq-cai\".getBasicFunc)","breadcrumbs":"Candid » func","id":"57","title":"func"},"58":{"body":"JavaScript classes that inherit from the Azle type Service correspond to the Candid service type and will become child classes capable of creating instances that can perform cross-canister calls at runtime. TypeScript: import { CallResult, Principal, $query, Result, Service, serviceQuery, serviceUpdate, $update\n} from 'azle'; class SomeService extends Service { @serviceQuery query1: () => CallResult; @serviceUpdate update1: () => CallResult;\n} $query;\nexport function getService(): SomeService { return new SomeService(Principal.fromText('aaaaa-aa'));\n} $update;\nexport async function callService( service: SomeService\n): Promise> { return await service.update1().call();\n} Candid: type ManualReply = variant { Ok : text; Err : text };\nservice : () -> { callService : ( service { query1 : () -> (bool) query; update1 : () -> (text) }, ) -> (ManualReply); getService : () -> ( service { query1 : () -> (bool) query; update1 : () -> (text) }, ) query;\n} dfx: dfx canister call candid_canister getService\n(service \"aaaaa-aa\")","breadcrumbs":"Candid » service","id":"58","title":"service"},"59":{"body":"The Azle type Principal corresponds to the Candid type principal and will become an @dfinity/principal at runtime. TypeScript: import { Principal, $query } from 'azle'; $query;\nexport function getPrincipal(): Principal { return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai');\n} $query;\nexport function printPrincipal(principal: Principal): Principal { console.log(typeof principal); return principal;\n} Candid: service : () -> { getPrincipal : () -> (principal) query; printPrincipal : (principal) -> (principal) query;\n} dfx: dfx canister call candid_canister printPrincipal '(principal \"rrkah-fqaaa-aaaaa-aaaaq-cai\")'\n(principal \"rrkah-fqaaa-aaaaa-aaaaq-cai\")","breadcrumbs":"Candid » principal","id":"59","title":"principal"},"6":{"body":"Azle intends to be a full TypeScript/JavaScript environment for the IC (a decentralized cloud platform), with support for all of the TypeScript/JavaScript language and as many relevant host APIs as possible. These host APIs will be similar to those available in the Node.js and web browser environments. One of the core benefits of Azle is that it allows web developers to bring their TypeScript/JavaScript skills to the IC. For example, Azle allows the use of various npm packages and VS Code intellisense. As for the IC, we believe its main benefits can be broken down into the following categories: Ownership Security Developer Experience Most of these benefits stem from the decentralized nature of the IC, though the IC is best thought of as a progressively decentralizing cloud platform. As opposed to traditional cloud platforms, its goal is to be owned and controlled by many independent entities. Ownership Full-stack group ownership Autonomous ownership Permanent APIs Credible neutrality Reduced platform risk Full-stack group ownership The IC allows you to build applications that are controlled directly and only (with some caveats) by a group of people. This is in opposition to most cloud applications written today, which must be under the control of a very limited number of people and often a single legal entity that answers directly to a cloud provider, which itself is a single legal entity. In the blockchain world, group-owned applications are known as DAOs . As opposed to DAOs built on most blockchains, the IC allows full-stack applications to be controlled by groups. This means that the group fully controls the running instances of the frontend and the backend code. Autonomous ownership In addition to allowing applications to be owned by groups of people, the IC also allows applications to be owned by no one. This essentially creates autonomous applications or everlasting processes that execute indefinitely. The IC will allow such an application to run until it depletes its balance of cycles, or until the NNS votes to shut it down. Permanent APIs Because most web APIs are owned and operated by individual entities, their fate is tied to that of their owners. If their owners go out of business, then those APIs may cease to exist. If their owners decide that they do not like or agree with certain users, they may restrict their access. In the end, they may decide to shut down or restrict access for arbitrary reasons. Because the IC allows for group and autonomous ownership of cloud software, the IC is able to produce potentially permanent web APIs. A decentralized group of independent entities will find it difficult to censor API consumers or shut down an API. An autonomous API would take those difficulties to the extreme, as it would continue operating as long as consumers were willing to pay for it. Credible neutrality Group and autonomous ownership makes it possible to build neutral cloud software on the IC. This type of software would allow independent parties to coordinate with reduced trust in each other or a single third-party coordinator. This removes the risk of the third-party coordinator acting in its own self-interest against the interests of the coordinating participants. The coordinating participants would also find it difficult to implement changes that would benefit themselves to the detriment of other participants. Examples could include mobile app stores, ecommerce marketplaces, and podcast directories. Reduced platform risk Because the IC is not owned or controlled by any one entity or individual, the risk of being deplatformed is reduced. This is in opposition to most cloud platforms, where the cloud provider itself generally has the power to arbitrarily remove users from its platform. While deplatforming can still occur on the IC, the only endogenous means of forcefully taking down an application is through an NNS vote. Security Built-in replication Built-in authentication Built-in firewall/port management Built-in sandboxing Threshold protocols Verifiable source code Blockchain integration Built-in replication Replication has many benefits that stem from reducing various central points of failure. The IC is at its core a Byzantine Fault Tolerant replicated compute environment. Applications are deployed to subnets which are composed of nodes running replicas. Each replica is an independent replicated state machine that executes an application's state transitions (usually initiated with HTTP requests) and persists the results. This replication provides a high level of security out-of-the-box. It is also the foundation of a number of protocols that provide threshold cryptographic operations to IC applications. Built-in authentication IC client tooling makes it easy to sign and send messages to the IC, and Internet Identity provides a novel approach to self-custody of private keys. The IC automatically authenticates messages with the public key of the signer, and provides a compact representation of that public key, called a principal, to the application. The principal can be used for authorization purposes. This removes many authentication concerns from the developer. Built-in firewall/port management The concept of ports and various other low-level network infrastructure on the IC is abstracted away from the developer. This can greatly reduce application complexity thus minimizing the chance of introducing vulnerabilities through incorrect configurations. Canisters expose endpoints through various methods, usually query or update methods. Because authentication is also built-in, much of the remaining vulnerability surface area is minimized to implementing correct authorization rules in the canister method endpoints. Built-in sandboxing Canisters have at least two layers of sandboxing to protect colocated canisters from each other. All canisters are at their core Wasm modules and thus inherit the built-in Wasm sandbox. In case there is any bug in the underlying implementation of the Wasm execution environment (or a vulnerability in the imported host functionality), there is also an OS-level sandbox. Developers need not do anything to take advantage of these sandboxes. Threshold protocols The IC provides a number of threshold protocols that allow groups of independent nodes to perform cryptographic operations. These protocols remove central points of failure while providing familiar and useful cryptographic operations to developers. Included are ECDSA , BLS , VRF-like , and in the future threshold key derivation . Verifiable source code IC applications (canisters) are compiled into Wasm and deployed to the IC as Wasm modules. The IC hashes each canister's Wasm binary and stores it for public retrieval. The Wasm binary hash can be retrieved and compared with the hash of an independently compiled Wasm binary derived from available source code. If the hashes match, then one can know with a high degree of certainty that the application is executing the Wasm binary that was compiled from that source code. Blockchain integration When compared with web APIs built for the same purpose, the IC provides a high degree of security when integrating with various other blockchains. It has a direct client integration with Bitcoin, allowing applications to query its state with BFT guarantees. A similar integration is coming for Ethereum. In addition to these blockchain client integrations, a threshold ECDSA protocol (tECDSA) allows the IC to create keys and sign transactions on various ECDSA chains . These chains include Bitcoin and Ethereum, and in the future the protocol may be extended to allow interaction with various EdDSA chains . These direct integrations combined with tECDSA provide a much more secure way to provide blockchain functionality to end users than creating and storing their private keys on traditional cloud infrastructure. Developer experience Built-in devops Orthogonal persistence Built-in devops The IC provides many devops benefits automatically. Though currently limited in its scalability, the protocol attempts to remove the need for developers to concern themselves with concepts such as autoscaling, load balancing, uptime, sandboxing, and firewalls/port management. Correctly constructed canisters have a simple deploy process and automatically inherit these devops capabilities up unto the current scaling limits of the IC. DFINITY engineers are constantly working to remove scalability bottlenecks. Orthogonal persistence The IC automatically persists its heap. This creates an extremely convenient way for developers to store application state, by simply writing into global variables in their programming language of choice. This is a great way to get started. If a canister upgrades its code, swapping out its Wasm binary, then the heap must be cleared. To overcome this limitation, there is a special area of memory called stable memory that persists across these canister upgrades. Special stable data structures provide a familiar API that allows writing into stable memory directly. All of this together provides the foundation for a very simple persistence experience for the developer. The persistence tools now available and coming to the IC may be simpler than their equivalents on traditional cloud infrastructure.","breadcrumbs":"Azle (Beta) » Benefits","id":"6","title":"Benefits"},"60":{"body":"The Azle type reserved corresponds to the Candid type reserved , is the TypeScript type any, and will become a JavaScript null at runtime. TypeScript: import { $query, reserved } from 'azle'; $query;\nexport function getReserved(): reserved { return 'anything';\n} $query;\nexport function printReserved(reserved: reserved): reserved { console.log(typeof reserved); return reserved;\n} Candid: service : () -> { getReserved : () -> (reserved) query; printReserved : (reserved) -> (reserved) query;\n} dfx: dfx canister call candid_canister printReserved '(null)'\n(null : reserved)","breadcrumbs":"Candid » reserved","id":"60","title":"reserved"},"61":{"body":"The Azle type empty corresponds to the Candid type empty and has no JavaScript value at runtime. TypeScript: import { empty, $query } from 'azle'; $query;\nexport function getEmpty(): empty { throw 'Anything you want';\n} // Note: It is impossible to call this function because it requires an argument\n// but there is no way to pass an \"empty\" value as an argument.\n$query;\nexport function printEmpty(empty: empty): empty { console.log(typeof empty); throw 'Anything you want';\n} Candid: service : () -> { getEmpty : () -> (empty) query; printEmpty : (empty) -> (empty) query;\n} dfx: dfx canister call candid_canister printEmpty '(\"You can put anything here\")'\nError: Failed to create argument blob.\nCaused by: Failed to create argument blob. Invalid data: Unable to serialize Candid values: type mismatch: \"You can put anything here\" cannot be of type empty","breadcrumbs":"Candid » empty","id":"61","title":"empty"},"62":{"body":"","breadcrumbs":"Stable Structures » Stable Structures","id":"62","title":"Stable Structures"},"63":{"body":"64 GiB of stable memory Persistent across upgrades Familiar API Must specify memory id Must specify maximum key size Must specify maximum value size No migrations per memory id Stable structures are data structures with familiar APIs that allow access to stable memory. Stable memory is a separate memory location from the heap that currently allows up to 64 GiB of storage. Stable memory persists automatically across upgrades. Persistence on the Internet Computer (IC) is very important to understand. When a canister is upgraded (its code is changed after being initially deployed) its heap is wiped. This includes all global variables. On the other hand, anything stored in stable memory will be preserved. Writing and reading to and from stable memory can be done with a low-level API , but it is generally easier and preferable to use stable structures. Azle currently provides one stable structure called StableBTreeMap. It's similar to a JavaScript Map and has most of the common operations you'd expect such as reading, inserting, and removing values. Here's how to define a simple StableBTreeMap. Each StableBTreeMap must be defined in the global scope (not within any functions or objects etc): import { nat8, StableBTreeMap } from 'azle'; let map = new StableBTreeMap(0, 100, 1_000); This is a StableBTreeMap with a key of type nat8 and a value of type string. Key and value types can be any Candid type . This StableBTreeMap also has a memory id of 0, a maximum key size of 100 bytes and a maximum value size of 1_000 bytes. You must statically specify the memory id, maximum key size, and maximum value sizes (they cannot be variables). Each StableBTreeMap instance must have a unique memory id. Once a memory id is allocated, it cannot be used with a different StableBTreeMap. This means you can't create another StableBTreeMap using the same memory id, and you can't change the key or value types of an existing StableBTreeMap. This problem will be addressed . Here's an example showing all of the basic StableBTreeMap operations: import { Alias, nat64, nat8, Opt, $query, StableBTreeMap, Tuple, $update, Vec\n} from 'azle'; type Key = Alias;\ntype Value = Alias; let map = new StableBTreeMap(0, 100, 1_000); $query;\nexport function containsKey(key: Key): boolean { return map.containsKey(key);\n} $query;\nexport function get(key: Key): Opt { return map.get(key);\n} $update;\nexport function insert(key: Key, value: Value): Opt { return map.insert(key, value);\n} $query;\nexport function isEmpty(): boolean { return map.isEmpty();\n} $query;\nexport function items(): Vec> { return map.items();\n} $query;\nexport function keys(): Vec { return map.keys();\n} $query;\nexport function len(): nat64 { return map.len();\n} $update;\nexport function remove(key: Key): Opt { return map.remove(key);\n} $query;\nexport function values(): Vec { return map.values();\n} With these basic operations you can build more complex CRUD database applications: import { blob, ic, nat64, Opt, Principal, $query, Record, Result, StableBTreeMap, $update, Variant, Vec, match\n} from 'azle'; type User = Record<{ id: Principal; createdAt: nat64; recordingIds: Vec; username: string;\n}>; type Recording = Record<{ id: Principal; audio: blob; createdAt: nat64; name: string; userId: Principal;\n}>; let users = new StableBTreeMap(0, 38, 100_000);\nlet recordings = new StableBTreeMap(1, 38, 5_000_000); $update;\nexport function createUser(username: string): User { const id = generateId(); const user: User = { id, createdAt: ic.time(), recordingIds: [], username }; users.insert(user.id, user); return user;\n} $query;\nexport function readUsers(): Vec { return users.values();\n} $query;\nexport function readUserById(id: Principal): Opt { return users.get(id);\n} $update;\nexport function deleteUser(id: Principal): Result< User, Variant<{ UserDoesNotExist: Principal; }>\n> { const user = users.get(id); return match(user, { Some: (user) => { user.recordingIds.forEach((recordingId) => { recordings.remove(recordingId); }); users.remove(user.id); return { Ok: user }; }, None: () => { return { Err: { UserDoesNotExist: id } }; } });\n} $update;\nexport function createRecording( audio: blob, name: string, userId: Principal\n): Result< Recording, Variant<{ UserDoesNotExist: Principal; }>\n> { const user = users.get(userId); return match(user, { Some: (user) => { const id = generateId(); const recording: Recording = { id, audio, createdAt: ic.time(), name, userId }; recordings.insert(recording.id, recording); const updatedUser: User = { ...user, recordingIds: [...user.recordingIds, recording.id] }; users.insert(updatedUser.id, updatedUser); return { Ok: recording }; }, None: () => { return { Err: { UserDoesNotExist: userId } }; } });\n} $query;\nexport function readRecordings(): Vec { return recordings.values();\n} $query;\nexport function readRecordingById(id: Principal): Opt { return recordings.get(id);\n} $update;\nexport function deleteRecording(id: Principal): Result< Recording, Variant<{ RecordingDoesNotExist: Principal; UserDoesNotExist: Principal; }>\n> { const recording = recordings.get(id); return match(recording, { Some: (recording) => { const user = users.get(recording.userId); return match(user, { Some: (user) => { const updatedUser: User = { ...user, recordingIds: user.recordingIds.filter( (recordingId) => recordingId.toText() !== recording.id.toText() ) }; users.insert(updatedUser.id, updatedUser); recordings.remove(id); return { Ok: recording }; }, None: () => { return { Err: { UserDoesNotExist: recording.userId } }; } }); }, None: () => { return { Err: { RecordingDoesNotExist: id } }; } });\n} function generateId(): Principal { const randomBytes = new Array(29) .fill(0) .map((_) => Math.floor(Math.random() * 256)); return Principal.fromUint8Array(Uint8Array.from(randomBytes));\n} The example above shows a very basic audio recording backend application. There are two types of entities that need to be stored, User and Recording. These are represented as Candid records. Each entity gets its own StableBTreeMap: import { blob, nat64, Principal, Record, StableBTreeMap, Vec } from 'azle'; type User = Record<{ id: Principal; createdAt: nat64; recordingIds: Vec; username: string;\n}>; type Recording = Record<{ id: Principal; audio: blob; createdAt: nat64; name: string; userId: Principal;\n}>; let users = new StableBTreeMap(0, 38, 100_000);\nlet recordings = new StableBTreeMap(1, 38, 5_000_000); Notice that each StableBTreeMap has a unique memory id. The maximum key and value sizes are also set according to the expected application usage. You can figure out the appropriate maximum key and value sizes by reasoning about your application and engaging in some trial and error using the insert method. Calling insert on a StableBTreeMap will throw an error which in some cases will have the information that you need to determine the maximum key or value size. If you attempt to insert a key or value that is too large, the KeyTooLarge and ValueTooLarge errors will show you the size of the value that you attempted to insert. You can increase the maximum key or value size based on the information you receive from the KeyTooLarge and ValueTooLarge errors and try inserting again. Thus through some trial and error you can whittle your way to a correct solution. In some cases all of your values will have an obvious static maximum size. In the audio recording example, trial and error revealed that Principal is most likely always 38 bytes, thus the maximum key size is set to 38. Maximum value sizes can be more tricky to figure out, especially if the values are records or variants with dynamic fields such as arrays. User has one such dynamic field, recordingIds. Since each recording id is a Principal, we know that each will take up 38 bytes. The other fields on User shouldn't take up too many bytes so we'll ignore them for our analysis. We've set the maximum value size of User to be 100_000 bytes. If we divide 100_00 by 38, we get ~2_631. This will result in each user being able to store around that many recordings. That's acceptable for our example, and so we'll go with it. As for Recording, the largest dynamic field is audio, which will be the actual bytes of the audio recording. We've set the maximum value size here to 5_000_000, which should allow for recordings of ~5 MB in size. That seems reasonable for our example, and so we'll go with it. As you can see, finding the correct maximum key and value sizes is a bit of an art right now. Combining some trial and error with reasoning about your specific application should get you a working solution in most cases. It's our hope that the need to specify maximum key and value sizes will be removed in the future.","breadcrumbs":"Stable Structures » TLDR","id":"63","title":"TLDR"},"64":{"body":"","breadcrumbs":"Stable Structures » Caveats","id":"64","title":"Caveats"},"65":{"body":"You should be wary when using a float64, float32, Service, or Func in any type that is a key for a stable structure. These types do not have the ability to be strictly ordered in all cases. Service and Func will have no order. float64 and float32 will treat NaN as less than any other type. These caveats may impact key performance.","breadcrumbs":"Stable Structures » Keys","id":"65","title":"Keys"},"66":{"body":"Examples: async_await bitcoin composite_queries cross_canister_calls cycles ethereum_json_rpc func_types heartbeat inline_types ledger_canister management_canister outgoing_http_requests threshold_ecdsa rejections timers tuple_types whoami Canisters are generally able to call the query or update methods of other canisters in any subnet. We refer to these types of calls as cross-canister calls. A cross-canister call begins with a definition of the canister to be called, referred to as a service. Imagine a simple service called token_canister: import { ic, match, nat64, Principal, StableBTreeMap, $update } from 'azle'; let accounts = new StableBTreeMap(0, 38, 15); $update;\nexport function transfer(to: Principal, amount: nat64): nat64 { const from = ic.caller(); const fromBalance = match(accounts.get(from), { Some: (some) => some, None: () => 0n }); const toBalance = match(accounts.get(to), { Some: (some) => some, None: () => 0n }); accounts.insert(from, fromBalance - amount); accounts.insert(to, toBalance + amount); return amount;\n} Here's how you would create its service definition: import { CallResult, Principal, nat64, Service, serviceUpdate } from 'azle'; class TokenCanister extends Service { @serviceUpdate transfer: (to: Principal, amount: nat64) => CallResult;\n} Once you have a service definition you can instantiate it with the canister's Principal and then invoke its methods. Here's how to instantiate TokenCanister: const tokenCanister = new TokenCanister( Principal.fromText('r7inp-6aaaa-aaaaa-aaabq-cai')\n); And here's a more complete example of a canister called payout_canister that performs a cross-canister call to token_canister: import { CallResult, nat64, Principal, Result, Service, serviceUpdate, $update\n} from 'azle'; class TokenCanister extends Service { @serviceUpdate transfer: (to: Principal, amount: nat64) => CallResult;\n} const tokenCanister = new TokenCanister( Principal.fromText('r7inp-6aaaa-aaaaa-aaabq-cai')\n); $update;\nexport async function payout( to: Principal, amount: nat64\n): Promise> { return await tokenCanister.transfer(to, amount).call();\n} Notice that the tokenCanister.transfer method, because it is a cross-canister method, returns a CallResult. All cross-canister calls return CallResult, which has an Ok or Err property depending on if the cross-canister call was successful or not. The IC guarantees that cross-canister calls will return. This means that, generally speaking, you will always receive a CallResult. Azle does not throw on cross-canister calls. Wrapping your cross-canister call in a try...catch most likely won't do anything useful. Let's add to our example code and explore adding some practical result-based error-handling to stop people from stealing tokens. token_canister: import { ic, match, nat64, Principal, Result, StableBTreeMap, $update, Variant\n} from 'azle'; let accounts = new StableBTreeMap(0, 38, 15); $update;\nexport function transfer( to: Principal, amount: nat64\n): Variant< Result< nat64, Variant<{ InsufficientBalance: nat64; }> >\n> { const from = ic.caller(); const fromBalance = match(accounts.get(from), { Some: (some) => some, None: () => 0n }); if (fromBalance < amount) { return { Err: { InsufficientBalance: fromBalance } }; } const toBalance = match(accounts.get(to), { Some: (some) => some, None: () => 0n }); accounts.insert(from, fromBalance - amount); accounts.insert(to, toBalance + amount); return { Ok: amount };\n} payout_canister: import { CallResult, match, nat64, Principal, Result, Service, serviceUpdate, $update, Variant\n} from 'azle'; class TokenCanister extends Service { @serviceUpdate transfer: ( to: Principal, amount: nat64 ) => CallResult< Result< nat64, Variant<{ InsufficientBalance: nat64; }> > >;\n} const tokenCanister = new TokenCanister( Principal.fromText('r7inp-6aaaa-aaaaa-aaabq-cai')\n); $update;\nexport async function payout( to: Principal, amount: nat64\n): Promise> { const callResult = await tokenCanister.transfer(to, amount).call(); return match(callResult, { Ok: (transferResult) => match(transferResult, { Ok: (ok) => ({ Ok: ok }), Err: (err) => ({ Err: JSON.stringify(err) }) }), Err: (err) => ({ Err: err }) });\n} Azle provides a match function that will help you handle variant branches. This provides some benefits over using in, such as if ('Err' in result) or if ('Ok' in result). There are other ways to check for the Ok or Err properties as well, feel free to experiment with the way that you prefer. They all have trade-offs. So far we have only shown a cross-canister call from an update method. Update methods can call other update methods or query methods (but not composite query methods as discussed below). If an update method calls a query method, that query method will be called in replicated mode. Replicated mode engages the consensus process, but for queries the state will still be discarded. Cross-canister calls can also be initiated from query methods. These are known as composite queries, and in Azle they are simply async query methods. Composite queries can call other composite query methods and regular query methods. Composite queries cannot call update methods. Here's an example of a composite query method: import { CallResult, Principal, $query, Result, Service, serviceQuery\n} from 'azle'; class SomeCanister extends Service { @serviceQuery queryForBoolean: () => CallResult;\n} const someCanister = new SomeCanister( Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')\n); $query;\nexport async function querySomeCanister(): Promise> { return await someCanister.queryForBoolean().call();\n} You can expect cross-canister calls within the same subnet to take up to a few seconds to complete, and cross-canister calls across subnets take about double that time . If you don't need to wait for your cross-canister call to return, you can use notify: import { CallResult, Principal, RejectionCode, Result, Service, serviceUpdate, $update\n} from 'azle'; class SomeCanister extends Service { @serviceUpdate receiveNotification: () => CallResult;\n} const someCanister = new SomeCanister( Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')\n); $update;\nexport function sendNotification(): Result { return someCanister.receiveNotification().notify();\n} If you need to send cycles with your cross-canister call, you can call cycles before calling call or notify: import { CallResult, Principal, RejectionCode, Result, Service, serviceUpdate, $update\n} from 'azle'; class SomeCanister extends Service { @serviceUpdate receiveNotification: () => CallResult;\n} const someCanister = new SomeCanister( Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')\n); $update;\nexport function sendNotification(): Result { return someCanister.receiveNotification().cycles(1_000_000n).notify();\n}","breadcrumbs":"Cross-canister » Cross-canister","id":"66","title":"Cross-canister"},"67":{"body":"This chapter is a work in progress.","breadcrumbs":"HTTP » HTTP","id":"67","title":"HTTP"},"68":{"body":"Examples: http_counter import { blob, Func, nat16, Opt, $query, Query, Record, Tuple, Variant, Vec\n} from 'azle'; type HttpRequest = Record<{ method: string; url: string; headers: Vec; body: blob;\n}>; type HttpResponse = Record<{ status_code: nat16; headers: Vec; body: blob; streaming_strategy: Opt; upgrade: Opt;\n}>; type Header = Tuple<[string, string]>; type StreamingStrategy = Variant<{ Callback: CallbackStrategy;\n}>; type CallbackStrategy = Record<{ callback: Callback; token: Token;\n}>; type Callback = Func StreamingCallbackHttpResponse>>; type StreamingCallbackHttpResponse = Record<{ body: blob; token: Opt;\n}>; type Token = Record<{ arbitrary_data: string;\n}>; $query;\nexport function http_request(req: HttpRequest): HttpResponse { return { status_code: 200, headers: [], body: Uint8Array.from([]), streaming_strategy: Opt.None, upgrade: Opt.Some(false) };\n}","breadcrumbs":"HTTP » Incoming HTTP requests","id":"68","title":"Incoming HTTP requests"},"69":{"body":"Examples: ethereum_json_rpc outgoing_http_requests import { Alias, ic, $init, match, nat32, $query, StableBTreeMap, $update, Opt\n} from 'azle';\nimport { HttpResponse, HttpTransformArgs, managementCanister\n} from 'azle/canisters/management';\nimport decodeUtf8 from 'decode-utf8';\nimport encodeUtf8 from 'encode-utf8'; type JSON = Alias