Module for working with Blobs (immutable sequences of bytes).
Blobs represent sequences of bytes. They are immutable, iterable, but not indexable and can be empty.
Byte sequences are also often represented as [Nat8]
, i.e. an array of bytes, but this representation is currently much less compact than Blob
, taking 4 physical bytes to represent each logical byte in the sequence.
If you would like to manipulate Blobs, it is recommended that you convert
Blobs to [var Nat8]
or Buffer<Nat8>
, do the manipulation, then convert back.
Import from the base library to use this module.
motoko name=import
import Blob "mo:base/Blob";
Some built in features not listed in this module:
Blob
literal from a Text
literal, provided the context expects an expression of type Blob
.b.size() : Nat
returns the number of bytes in the blob b
;b.values() : Iter.Iter<Nat8>
returns an iterator to enumerate the bytes of the blob b
.For example:
motoko include=import
import Debug "mo:base/Debug";
import Nat8 "mo:base/Nat8";
let blob = "\00\00\00\ff" : Blob; // blob literals, where each byte is delimited by a back-slash and represented in hex
let blob2 = "charsもあり" : Blob; // you can also use characters in the literals
let numBytes = blob.size(); // => 4 (returns the number of bytes in the Blob)
for (byte : Nat8 in blob.values()) { // iterator over the Blob
Debug.print(Nat8.toText(byte))
}
Boolean type and operations.
While boolean operators _ and _
and _ or _
are short-circuiting,
avoiding computation of the right argument when possible, the functions
logand(_, _)
and logor(_, _)
are strict and will always evaluate both
of their arguments.
Certified data.
The Internet Computer allows canister smart contracts to store a small amount of data during update method processing so that during query call processing, the canister can obtain a certificate about that data.
This module provides a low-level interface to this API, aimed at advanced users and library implementors. See the Internet Computer Functional Specification and corresponding documentation for how to use this to make query calls to your canister tamperproof.
Utilities for Char
(character)
Managing cycles within actors in the Internet Computer Protocol (ICP).
The usage of the Internet Computer is measured, and paid for, in cycles. This library provides imperative operations for observing cycles, transferring cycles, and observing refunds of cycles.
NOTE: Since cycles measure computational resources, the value of balance()
can change from one call to the next.
Cycles can be transferred from the current actor to another actor with the evaluation of certain forms of expression.
In particular, the expression must be a call to a shared function, a call to a local function with an async
return type, or a simple async
expression.
To attach an amount of cycles to an expression <exp>
, simply prefix the expression with (with cycles = <amount>)
, that is, (with cycles = <amount>) <exp>
.
NOTE: Attaching cycles will trap if the amount specified exceeds 2 ** 128
cycles.
Upon the call, but not before, the amount of cycles is deducted from balance()
.
If this total exceeds balance()
, the caller traps, aborting the call without consuming the cycles.
Note that attaching cycles to a call to a local function call or async
expression just transfers cycles from the current actor to itself.
Example for use on the ICP:
motoko no-repl
import Cycles "mo:base/Cycles";
import Debug "mo:base/Debug";
actor {
public func main() : async() {
Debug.print("Main balance: " # debug_show(Cycles.balance()));
await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles
Debug.print("Main refunded: " # debug_show(Cycles.refunded())); // 5_000_000
Debug.print("Main balance: " # debug_show(Cycles.balance())); // decreased by around 10_000_000
};
func operation() : async() {
Debug.print("Operation balance: " # debug_show(Cycles.balance()));
Debug.print("Operation available: " # debug_show(Cycles.available()));
let obtained = Cycles.accept<system>(10_000_000);
Debug.print("Operation obtained: " # debug_show(obtained)); // => 10_000_000
Debug.print("Operation balance: " # debug_show(Cycles.balance())); // increased by 10_000_000
Debug.print("Operation available: " # debug_show(Cycles.available())); // decreased by 10_000_000
}
}
Utility functions for debugging.
Import from the base library to use this module.
motoko name=import
import Debug "mo:base/Debug";
Error values and inspection.
The Error
type is the argument to throw
, parameter of catch
.
The Error
type is opaque.
Double precision (64-bit) floating-point numbers in IEEE 754 representation.
This module contains common floating-point constants and utility functions.
Notation for special values in the documentation below:
+inf
: Positive infinity
-inf
: Negative infinity
NaN
: "not a number" (can have different sign bit values, but NaN != NaN
regardless of the sign).
Note: Floating point numbers have limited precision and operations may inherently result in numerical errors.
Examples of numerical errors:
0.1 + 0.1 + 0.1 == 0.3 // => false
1e16 + 1.0 != 1e16 // => false
(and many more cases)
Advice:
==
or !=
are discouraged. Instead, it is better to compare
floating-point numbers with a numerical tolerance, called epsilon.Example:
import Float "mo:base/Float";
let x = 0.1 + 0.1 + 0.1;
let y = 0.3;
let epsilon = 1e-6; // This depends on the application case (needs a numerical error analysis).
Float.equal(x, y, epsilon) // => true
NaN sign:
abs
, neg
, and copySign
. Other operations can have an arbitrary
sign bit for NaN results.Functions on functions, creating functions from simpler inputs.
(Most commonly used when programming in functional style using higher-order functions.)
Signed integer numbers with infinite precision (also called big integers).
Most operations on integer numbers (e.g. addition) are available as built-in operators (e.g. -1 + 1
).
This module provides equivalent functions and Text
conversion.
Import from the base library to use this module.
motoko name=import
import Int "mo:base/Int";
Utility functions on 16-bit signed integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Int16 "mo:base/Int16";
Utility functions on 32-bit signed integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Int32 "mo:base/Int32";
Utility functions on 64-bit signed integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Int64 "mo:base/Int64";
Utility functions on 8-bit signed integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Int8 "mo:base/Int8";
Low-level interface to the Internet Computer.
Utilities for Iter
(iterator) values.
Iterators are a way to represent sequences of values that can be lazily produced. They can be used to:
Iterators are inherently stateful. Calling next
"consumes" a value from
the Iterator that cannot be put back, so keep that in mind when sharing
iterators between consumers.
An iterator i
can be iterated over using
for (x in i) {
…do something with x…
}
Iterators can be:
values
or keys
function on a Map
) or from scratch (e.g. using empty
or singleton
).map
, filter
, concat
, etc. Which can be used to compose several transformations together without materializing intermediate collections.forEach
, size
, toArray
, etc.concat
.Resizable array with O(sqrt(n))
memory overhead.
Static type List
that can be declared stable
.
For the List
class see the file Class.mo.
This implementation is adapted with permission from the vector
Mops package created by Research AG.
Copyright: 2023 MR Research AG Main author: Andrii Stepanov Contributors: Timo Hanke (timohanke), Andy Gura (andygura), react0r-com
An imperative key-value map based on order/comparison of the keys. The map data structure type is stable and can be used for orthogonal persistence.
Example:
import Map "mo:base/Map";
import Nat "mo:base/Nat";
persistent actor {
let numberNames = Map.empty<Nat, Text>();
Map.add(numberNames, Nat.compare, 0, "Zero");
Map.add(numberNames, Nat.compare, 1, "One");
Map.add(numberNames, Nat.compare, 2, "Two");
}
The internal implementation is a B-tree with order 32.
Performance:
O(log(n))
worst case cost per insertion, removal, and retrieval operation.O(n)
for storing the entire map.
n
denotes the number of key-value entries stored in the map.Natural numbers with infinite precision.
Most operations on natural numbers (e.g. addition) are available as built-in operators (e.g. 1 + 1
).
This module provides equivalent functions and Text
conversion.
Import from the base library to use this module.
motoko name=import
import Nat "mo:base/Nat";
Utility functions on 16-bit unsigned integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Nat16 "mo:base/Nat16";
Utility functions on 32-bit unsigned integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Nat32 "mo:base/Nat32";
Utility functions on 64-bit unsigned integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Nat64 "mo:base/Nat64";
Utility functions on 8-bit unsigned integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Nat8 "mo:base/Nat8";
Typesafe nullable values.
Optional values can be seen as a typesafe null
. A value of type ?Int
can
be constructed with either null
or ?42
. The simplest way to get at the
contents of an optional is to use pattern matching:
let optionalInt1 : ?Int = ?42;
let optionalInt2 : ?Int = null;
let int1orZero : Int = switch optionalInt1 {
case null 0;
case (?int) int;
};
assert int1orZero == 42;
let int2orZero : Int = switch optionalInt2 {
case null 0;
case (?int) int;
};
assert int2orZero == 0;
The functions in this module capture some common operations when working with optionals that can be more succinct than using pattern matching.
Utilities for Order
(comparison between two values).
Module for interacting with Principals (users and canisters).
Principals are used to identify entities that can interact with the Internet Computer. These entities are either users or canisters.
Example textual representation of Principals:
un4fu-tqaaa-aaaab-qadjq-cai
In Motoko, there is a primitive Principal type called Principal
. As an example
of where you might see Principals, you can access the Principal of the
caller of your shared function.
motoko no-repl
shared(msg) func foo() {
let caller : Principal = msg.caller;
};
Then, you can use this module to work with the Principal
.
Import from the base library to use this module.
motoko name=import
import Principal "mo:base/Principal";
A mutable double-ended queue of elements. The queue has two ends, front and back. Elements can be added and removed at the two ends.
This can be used for different use cases, such as:
pushBack()
and popFront()
pushFront()
and popFront()
.Example:
import Queue "Queue";
import Debug "Debug";
persistent actor {
let orders = Queue.empty<Text>();
Queue.pushBack(orders, "Antipasta");
Queue.pushBack(orders, "Spaghetti");
Queue.pushBack(orders, "Bistecca");
Queue.pushBack(orders, "Dolce");
label iteration loop {
switch (Queue.popFront(orders)) {
case null { break iteration };
case (?description) {
Debug.print(description)
}
}
}
// prints:
// `Antipasta`
// `Spaghetti`
// `Bistecca`
// `Dolce`
}
The internal implementation is a doubly-linked list.
Performance:
O(1)
for push, pop, and peek operations.O(n)
.
n
denotes the number of elements stored in the queue.Random number generation.
Byte-level access to isolated, virtual stable memory regions.
This is a moderately lightweight abstraction over IC stable memory and supports persisting
regions of binary data across Motoko upgrades.
Use of this module is fully compatible with Motoko's use of
stable variables, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API.
It is also fully compatible with existing uses of the ExperimentalStableMemory
library, which has a similar interface, but,
only supported a single memory region, without isolation between different applications.
The Region
type is stable and can be used in stable data structures.
A new, empty Region
is allocated using function new()
.
Regions are stateful objects and can be distinguished by the numeric identifier returned by function id(region)
.
Every region owns an initially empty, but growable sequence of virtual IC stable memory pages.
The current size, in pages, of a region is returned by function size(region)
.
The size of a region determines the range, [ 0, ..., size(region)*2^16 ), of valid byte-offsets into the region; these offsets are used as the source and destination of load
/store
operations on the region.
Memory is allocated to a region, using function grow(region, pages)
, sequentially and on demand, in units of 64KiB logical pages, starting with 0 allocated pages.
A call to grow
may succeed, returning the previous size of the region, or fail, returning a sentinel value. New pages are zero initialized.
A size of a region can only grow and never shrink. In addition, the stable memory pages allocated to a region will not be reclaimed by garbage collection, even if the region object itself becomes unreachable.
Growth is capped by a soft limit on physical page count controlled by compile-time flag
--max-stable-pages <n>
(the default is 65536, or 4GiB).
Each load
operation loads from region relative byte address offset
in little-endian
format using the natural bit-width of the type in question.
The operation traps if attempting to read beyond the current region size.
Each store
operation stores to region relative byte address offset
in little-endian format using the natural bit-width of the type in question.
The operation traps if attempting to write beyond the current region size.
Text values can be handled by using Text.decodeUtf8
and Text.encodeUtf8
, in conjunction with loadBlob
and storeBlob
.
The current region allocation and region contents are preserved across upgrades.
NB: The IC's actual stable memory size (ic0.stable_size
) may exceed the
total page size reported by summing all regions sizes.
This (and the cap on growth) are to accommodate Motoko's stable variables and bookkeeping for regions.
Applications that plan to use Motoko stable variables sparingly or not at all can
increase --max-stable-pages
as desired, approaching the IC maximum (initially 8GiB, then 32Gib, currently 64Gib).
All applications should reserve at least one page for stable variable data, even when no stable variables are used.
Usage:
motoko no-repl
import Region "mo:base/Region";
Error handling with the Result type.
An imperative set based on order/comparison of the elements. A set is a collection of elements without duplicates. The set data structure type is stable and can be used for orthogonal persistence.
Example:
import Set "mo:base/Set";
import Nat "mo:base/Nat";
persistent actor {
let userIds = Set.empty<Nat>();
Set.add(userIds, Nat.compare, 1);
Set.add(userIds, Nat.compare, 2);
Set.add(userIds, Nat.compare, 3);
}
The internal implementation is a B-tree with order 32.
Performance:
O(log(n))
worst case cost per insertion, removal, and retrieval operation.O(n)
for storing the entire set.
n
denotes the number of elements stored in the set.A mutable stack data structure. Elements can be pushed on top of the stack and removed from top of the stack (LIFO).
Example:
import Stack "Stack";
import Debug "Debug";
persistent actor {
let levels = Stack.empty<Text>();
Stack.push(levels, "Inner");
Stack.push(levels, "Middle");
Stack.push(levels, "Outer");
label iteration loop {
switch (Stack.pop(levels)) {
case null { break iteration };
case (?name) {
Debug.print(name)
}
}
}
// prints:
// `Outer`
// `Middle`
// `Inner`
}
The internal implementation is a singly-linked list.
Performance:
O(1)
for push, pop, and peek operation.O(n)
.
n
denotes the number of elements stored on the stack.Utility functions for Text
values.
A Text
value represents human-readable text as a sequence of characters of type Char
.
let text = "Hello!";
let size = text.size(); // 6
let iter = text.chars(); // iterator ('H', 'e', 'l', 'l', 'o', '!')
let concat = text # " 👋"; // "Hello! 👋"
The "mo:base/Text"
module defines additional operations on Text
values.
Import the module from the base library:
motoko name=import
import Text "mo:base/Text";
Note: Text
values are represented as ropes of UTF-8 character sequences with O(1) concatenation.
System time utilities and timers.
The following example illustrates using the system time:
import Int = "mo:base/Int";
import Time = "mo:base/Time";
actor {
var lastTime = Time.now();
public func greet(name : Text) : async Text {
let now = Time.now();
let elapsedSeconds = (now - lastTime) / 1000_000_000;
lastTime := now;
return "Hello, " # name # "!" #
" I was last called " # Int.toText(elapsedSeconds) # " seconds ago";
};
};
Note: If moc
is invoked with -no-timer
, the importing will fail.
Note: The resolution of the timers is in the order of the block rate,
so durations should be chosen well above that. For frequent
canister wake-ups the heartbeat mechanism should be considered.
Timers for one-off or periodic tasks. Applicable as part of the default mechanism.
If moc
is invoked with -no-timer
, the importing will fail. Furthermore, if passed --trap-on-call-error
, a congested canister send queue may prevent timer expirations to execute at runtime. It may also deactivate the global timer.
The resolution of the timers is similar to the block rate, so durations should be chosen well above that. For frequent canister wake-ups, consider using the heartbeat mechanism; however, when possible, canisters should prefer timers.
The functionality described below is enabled only when the actor does not override it by declaring an explicit system func timer
.
Timers are not persisted across upgrades. One possible strategy
to re-establish timers after an upgrade is to use stable variables
in the post_upgrade
hook and distill necessary timer information
from there.
Using timers for security (e.g., access control) is strongly discouraged. Make sure to inform yourself about state-of-the-art dapp security. If you must use timers for security controls, be sure to consider reentrancy issues as well as the vanishing of timers on upgrades and reinstalls.
For further usage information for timers on the IC, please consult the documentation.
Contains modules for working with tuples of different sizes.
Usage: either import the whole module and use the TupleN
modules, or import the specific TupleN
module directly.
import Tuples "mo:new-base/Tuples"; // imports all TupleN modules
import Nat "mo:new-base/Nat";
import Text "mo:new-base/Text";
Tuples.Tuple2.swap((1, "hello"));
Tuples.Tuple3.toText((1, "hello", 3), Nat.toText, Text.toText, Nat.toText);
import { Tuple2, Tuple3 } "mo:new-base/Tuples";
import Nat "mo:new-base/Nat";
import Text "mo:new-base/Text";
Tuple2.swap((1, "hello"));
Tuple3.toText((1, "hello", 3), Nat.toText, Text.toText, Nat.toText);
Mutable array utilities.
Collection of pseudo-random number generators
The algorithms deliver deterministic statistical randomness, not cryptographic randomness.
Algorithm 1: 128-bit Seiran PRNG See: https://github.com/andanteyk/prng-seiran
Algorithm 2: SFC64 and SFC32 (Chris Doty-Humphrey’s Small Fast Chaotic PRNG) See: https://numpy.org/doc/stable/reference/random/bit_generators/sfc64.html
Copyright: 2023 MR Research AG Main author: react0r-com Contributors: Timo Hanke (timohanke)
Purely-functional, singly-linked lists.
A list of type List<T>
is either null
or an optional pair of a value of type T
and a tail, itself of type List<T>
.
To use this library, import it using:
motoko name=initialize
import List "mo:base/List";
Immutable, ordered key-value maps.
The map type is stable whenever the key and value types are stable, allowing map values to be stored in stable variables.
Keys are ordered by an explicit compare
function, which must be the same
across all operations on a given map.
Example:
import Map "mo:base/pure/Map";
import Nat "mo:base/Nat";
persistent actor {
// creation
let empty = Map.empty<Nat, Text>();
// insertion
let map1 = Map.add(empty, Nat.compare, 0, "Zero");
// retrieval
let null = Map.get(empty, Nat.compare, 0);
let ?"Zero" = Map.get(map1, Nat.compare, 0);
// deletion
let map2 = Map.delete(map1, Nat.compare, 0);
assert not Map.isEmpty(map1);
assert Map.isEmpty(map2);
}
The internal representation is a red-black tree.
A red-black tree is a balanced binary search tree ordered by the keys.
The tree data structure internally colors each of its nodes either red or black, and uses this information to balance the tree during the modifying operations.
Performance:
O(log(n))
worst case cost per insertion, removal, and retrieval operation.O(n)
for storing the entire tree.
n
denotes the number of key-value entries (i.e. nodes) stored in the tree.Note:
O(log(n))
temporary objects that become garbage.Credits:
The core of this implementation is derived from:
Double-ended queue of a generic element type T
.
The interface is purely functional, not imperative, and queues are immutable values. In particular, Queue operations such as push and pop do not update their input queue but, instead, return the value of the modified Queue, alongside any other data. The input queue is left unchanged.
Examples of use-cases:
Queue (FIFO) by using pushBack()
and popFront()
.
Stack (LIFO) by using pushFront()
and popFront()
.
A Queue is internally implemented as two lists, a head access list and a (reversed) tail access list, that are dynamically size-balanced by splitting.
Construction: Create a new queue with the empty<T>()
function.
Note on the costs of push and pop functions:
O(1)
amortized costs, O(n)
worst case cost per single call.O(1)
amortized costs, O(n)
worst case cost per single call.n
denotes the number of elements stored in the queue.
Stable ordered set implemented as a red-black tree.
A red-black tree is a balanced binary search tree ordered by the elements.
The tree data structure internally colors each of its nodes either red or black, and uses this information to balance the tree during modifying operations.
Performance:
O(log(n))
worst case cost per insertion, removal, and retrieval operation.O(n)
for storing the entire tree.
n
denotes the number of elements (i.e. nodes) stored in the tree.Credits:
The core of this implementation is derived from:
Provides extended utility functions on Arrays.
Note the difference between mutable and non-mutable arrays below.
WARNING: If you are looking for a list that can grow and shrink in size, it is recommended you use
List
for those purposes. Arrays must be created with a fixed size.Import from the base library to use this module.