Skip to content

Commit 8d5c6ec

Browse files
authored
refactor: migrate remaining ninja-devs examples to mo:core 2.4.0 (#1328)
* refactor: migrate remaining ninja-devs examples to mo:core 2.4.0 Migrates daily_planner, evm_block_explorer, filevault, flying_ninja, and tokenmania to mo:core 2.4.0 — dropping mo:base, applying persistent actor, context dot notation, and updated APIs (List, Queue, VarArray, Runtime.trap). Also fixes the icp-cli URL in flying_ninja's README. * docs: fix outdated icp-cli URL in flying_ninja BUILD.md * refactor: use local Order type alias in flying_ninja sort comparator * refactor: apply modern Motoko style to daily_planner backend * fix: restore Option import needed for context dot .get() in daily_planner
1 parent 21feaf4 commit 8d5c6ec

12 files changed

Lines changed: 143 additions & 130 deletions

File tree

motoko/daily_planner/backend/app.mo

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import Array "mo:base/Array";
2-
import Blob "mo:base/Blob";
3-
import Bool "mo:base/Bool";
4-
import Cycles "mo:base/ExperimentalCycles";
5-
import Int "mo:base/Int";
6-
import Iter "mo:base/Iter";
7-
import Text "mo:base/Text";
8-
import Nat "mo:base/Nat";
9-
import Result "mo:base/Result";
1+
import Array "mo:core/Array";
2+
import Blob "mo:core/Blob";
3+
import Iter "mo:core/Iter";
4+
import Option "mo:core/Option";
5+
import Text "mo:core/Text";
6+
import Nat "mo:core/Nat";
7+
import Result "mo:core/Result";
108
import JSON "mo:json";
11-
import Option "mo:base/Option";
129
import HashMap "mo:map/Map";
1310
import { thash } "mo:map/Map";
1411
import IC "ic:aaaaa-aa";
@@ -35,7 +32,7 @@ persistent actor DailyPlanner {
3532
public type AddNoteResult = Result.Result<Text, Text>;
3633

3734
// HashMap to store the data for each day.
38-
var dayData = HashMap.new<Text, DayData>();
35+
let dayData = HashMap.new<Text, DayData>();
3936

4037
// Query function to get data for a specific date.
4138
// Returns null if the date does not contain any data.
@@ -46,15 +43,13 @@ persistent actor DailyPlanner {
4643
// Query function to get data for an entire month.
4744
// Returns a
4845
public query func getMonthData(year : Nat, month : Nat) : async [(Text, DayData)] {
49-
let monthPrefix = Text.concat(Int.toText(year), "-" # Int.toText(month) # "-");
50-
Iter.toArray(
51-
Iter.filter(
52-
HashMap.entries(dayData),
53-
func((k, _) : (Text, DayData)) : Bool {
54-
Text.startsWith(k, #text monthPrefix);
55-
},
56-
)
57-
);
46+
let monthPrefix = year.toText() # "-" # month.toText() # "-";
47+
Iter.filter(
48+
HashMap.entries(dayData),
49+
func((k, _) : (Text, DayData)) : Bool {
50+
k.startsWith(#text monthPrefix);
51+
},
52+
).toArray();
5853
};
5954

6055
// Update function to add a new note
@@ -65,12 +60,12 @@ persistent actor DailyPlanner {
6560
};
6661

6762
let newNote : Note = {
68-
id = Array.size(currentData.notes);
63+
id = currentData.notes.size();
6964
content = content;
7065
isCompleted = false;
7166
};
7267

73-
let updatedNotes = Array.append(currentData.notes, [newNote]);
68+
let updatedNotes = currentData.notes.concat([newNote]);
7469
let updatedData : DayData = {
7570
notes = updatedNotes;
7671
onThisDay = currentData.onThisDay;
@@ -87,9 +82,8 @@ persistent actor DailyPlanner {
8782
switch (HashMap.get(dayData, thash, date)) {
8883
case null { /* Do nothing if no data for this date */ };
8984
case (?data) {
90-
let updatedNotes = Array.map<Note, Note>(
91-
data.notes,
92-
func(note) {
85+
let updatedNotes = data.notes.map(
86+
func(note : Note) : Note {
9387
if (note.id == noteId) {
9488
return {
9589
id = note.id;
@@ -119,9 +113,9 @@ persistent actor DailyPlanner {
119113

120114
// Perform HTTPS outcall only if needed.
121115
if (currentData.onThisDay == null) {
122-
let parts = Iter.toArray(Text.split(date, #char '-'));
123-
let month = Option.get(Nat.fromText(parts[1]), 1);
124-
let day = Option.get(Nat.fromText(parts[2]), 1);
116+
let parts = date.split(#char '-').toArray();
117+
let month = Nat.fromText(parts[1]).get(1);
118+
let day = Nat.fromText(parts[2]).get(1);
125119

126120
// Prepare the https request.
127121
// "transform" is used to specify how the HTTP response is processed before consensus tries to agree on a response.
@@ -144,10 +138,7 @@ persistent actor DailyPlanner {
144138
// Perform HTTPS outcall using roughly 100B cycles.
145139
// See https outcall cost calculator: https://7joko-hiaaa-aaaal-ajz7a-cai.icp0.io.
146140
// Unused cycles are returned.
147-
Cycles.add<system>(100_000_000_000);
148-
149-
// Execute the https outcall
150-
let http_response : IC.http_request_result = await IC.http_request(http_request);
141+
let http_response : IC.http_request_result = await (with cycles = 100_000_000_000) IC.http_request(http_request);
151142

152143
// Parse response into JSON
153144
let decoded_text : Text = switch (Text.decodeUtf8(http_response.body)) {

motoko/daily_planner/mops.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
# Motoko dependencies (https://mops.one/)
22

3+
[toolchain]
4+
moc = "1.5.1"
5+
36
[dependencies]
4-
base = "0.14.9"
5-
core = "1.0.0"
7+
core = "2.4.0"
68
json = "1.0.0"
79
map = "9.0.1"
10+
11+
[moc]
12+
# M0236: use context dot notation (e.g. map.get(k) instead of Map.get(map, compare, k))
13+
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
14+
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
15+
args = ["-W", "M0236", "-W", "M0237", "-W", "M0223"]

motoko/evm_block_explorer/backend/app.mo

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ import EvmRpc "canister:evm_rpc";
22
import IC "ic:aaaaa-aa";
33
import Sha256 "mo:sha2/Sha256";
44
import Base16 "mo:base16/Base16";
5-
import Debug "mo:base/Debug";
6-
import Blob "mo:base/Blob";
7-
import Text "mo:base/Text";
8-
import Cycles "mo:base/ExperimentalCycles";
5+
import Runtime "mo:core/Runtime";
6+
import Text "mo:core/Text";
97

108
persistent actor EvmBlockExplorer {
119
transient let key_name = "test_key_1"; // Use "key_1" for production and "dfx_test_key" locally
@@ -32,8 +30,7 @@ persistent actor EvmBlockExplorer {
3230
// };
3331

3432
// Call `eth_getBlockByNumber` RPC method (unused cycles will be refunded)
35-
Cycles.add<system>(10_000_000_000);
36-
let result = await EvmRpc.eth_getBlockByNumber(services, null, #Number(height));
33+
let result = await (with cycles = 10_000_000_000) EvmRpc.eth_getBlockByNumber(services, null, #Number(height));
3734

3835
switch result {
3936
// Consistent, successful results.
@@ -42,11 +39,11 @@ persistent actor EvmBlockExplorer {
4239
};
4340
// All RPC providers return the same error.
4441
case (#Consistent(#Err error)) {
45-
Debug.trap("Error: " # debug_show error);
42+
Runtime.trap("Error: " # debug_show error);
4643
};
4744
// Inconsistent results between RPC providers. Should not happen if a single RPC provider is used.
4845
case (#Inconsistent(results)) {
49-
Debug.trap("Inconsistent results" # debug_show results);
46+
Runtime.trap("Inconsistent results" # debug_show results);
5047
};
5148
};
5249
};
@@ -62,8 +59,7 @@ persistent actor EvmBlockExplorer {
6259

6360
public func sign_message_with_ecdsa(message : Text) : async Text {
6461
let message_hash : Blob = Sha256.fromBlob(#sha256, Text.encodeUtf8(message));
65-
Cycles.add<system>(25_000_000_000);
66-
let { signature } = await IC.sign_with_ecdsa({
62+
let { signature } = await (with cycles = 25_000_000_000) IC.sign_with_ecdsa({
6763
message_hash;
6864
derivation_path = [];
6965
key_id = { curve = #secp256k1; name = key_name };
@@ -81,8 +77,7 @@ persistent actor EvmBlockExplorer {
8177
};
8278

8379
public func sign_message_with_schnorr(message : Text) : async Text {
84-
Cycles.add<system>(25_000_000_000);
85-
let { signature } = await IC.sign_with_schnorr({
80+
let { signature } = await (with cycles = 25_000_000_000) IC.sign_with_schnorr({
8681
message = Text.encodeUtf8(message);
8782
derivation_path = [];
8883
key_id = { algorithm = #ed25519; name = key_name };
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
# Motoko dependencies (https://mops.one/)
22

3+
[toolchain]
4+
moc = "1.5.1"
5+
36
[dependencies]
4-
base = "0.14.9"
5-
core = "1.0.0"
7+
core = "2.4.0"
68
sha2 = "0.1.0"
79
base16 = "1.0.0"
10+
11+
[moc]
12+
# M0236: use context dot notation (e.g. map.get(k) instead of Map.get(map, compare, k))
13+
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
14+
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
15+
args = ["-W", "M0236", "-W", "M0237", "-W", "M0223"]

motoko/filevault/backend/app.mo

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import Bool "mo:base/Bool";
2-
import Array "mo:base/Array";
3-
import Blob "mo:base/Blob";
1+
import Array "mo:core/Array";
2+
import Blob "mo:core/Blob";
43
import HashMap "mo:map/Map";
54
import { phash; thash } "mo:map/Map";
6-
import Iter "mo:base/Iter";
7-
import Nat "mo:base/Nat";
8-
import Principal "mo:base/Principal";
9-
import Text "mo:base/Text";
10-
import Option "mo:base/Option";
5+
import Iter "mo:core/Iter";
6+
import Option "mo:core/Option";
7+
import Principal "mo:core/Principal";
8+
import Text "mo:core/Text";
119

1210
persistent actor Filevault {
1311

@@ -29,7 +27,7 @@ persistent actor Filevault {
2927
type UserFiles = HashMap.Map<Text, File>;
3028

3129
// HashMap to store the user data
32-
private var files = HashMap.new<Principal, UserFiles>();
30+
private let files = HashMap.new<Principal, UserFiles>();
3331

3432
// Return files associated with a user's principal.
3533
private func getUserFiles(user : Principal) : UserFiles {
@@ -45,7 +43,7 @@ persistent actor Filevault {
4543

4644
// Check if a file name already exists for the user.
4745
public shared (msg) func checkFileExists(name : Text) : async Bool {
48-
Option.isSome(HashMap.get(getUserFiles(msg.caller), thash, name));
46+
HashMap.get(getUserFiles(msg.caller), thash, name).isSome();
4947
};
5048

5149
// Upload a file in chunks.
@@ -58,7 +56,7 @@ persistent actor Filevault {
5856
let _ = HashMap.put(userFiles, thash, name, { name = name; chunks = [fileChunk]; totalSize = chunk.size(); fileType = fileType });
5957
};
6058
case (?existingFile) {
61-
let updatedChunks = Array.append(existingFile.chunks, [fileChunk]);
59+
let updatedChunks = existingFile.chunks.concat([fileChunk]);
6260
let _ = HashMap.put(
6361
userFiles,
6462
thash,
@@ -76,18 +74,15 @@ persistent actor Filevault {
7674

7775
// Return list of files for a user.
7876
public shared (msg) func getFiles() : async [{ name : Text; size : Nat; fileType : Text }] {
79-
Iter.toArray(
80-
Iter.map(
81-
HashMap.vals(getUserFiles(msg.caller)),
82-
func(file : File) : { name : Text; size : Nat; fileType : Text } {
83-
{
84-
name = file.name;
85-
size = file.totalSize;
86-
fileType = file.fileType;
87-
};
88-
}
89-
)
90-
);
77+
HashMap.vals(getUserFiles(msg.caller)).map(
78+
func(file : File) : { name : Text; size : Nat; fileType : Text } {
79+
{
80+
name = file.name;
81+
size = file.totalSize;
82+
fileType = file.fileType;
83+
};
84+
}
85+
).toArray();
9186
};
9287

9388
// Return total chunks for a file
@@ -103,7 +98,7 @@ persistent actor Filevault {
10398
switch (HashMap.get(getUserFiles(msg.caller), thash, name)) {
10499
case null null;
105100
case (?file) {
106-
switch (Array.find(file.chunks, func(chunk : FileChunk) : Bool { chunk.index == index })) {
101+
switch (file.chunks.find(func(chunk : FileChunk) : Bool { chunk.index == index })) {
107102
case null null;
108103
case (?foundChunk) ?foundChunk.chunk;
109104
};
@@ -121,6 +116,6 @@ persistent actor Filevault {
121116

122117
// Delete a file.
123118
public shared (msg) func deleteFile(name : Text) : async Bool {
124-
Option.isSome(HashMap.remove(getUserFiles(msg.caller), thash, name));
119+
HashMap.remove(getUserFiles(msg.caller), thash, name).isSome();
125120
};
126121
};

motoko/filevault/mops.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Motoko dependencies (https://mops.one/)
22

3+
[toolchain]
4+
moc = "1.5.1"
5+
36
[dependencies]
4-
base = "0.14.9"
5-
core = "1.0.0"
7+
core = "2.4.0"
68
map = "9.0.1"
9+
10+
[moc]
11+
# M0236: use context dot notation (e.g. map.get(k) instead of Map.get(map, compare, k))
12+
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
13+
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
14+
args = ["-W", "M0236", "-W", "M0237", "-W", "M0223"]

motoko/flying_ninja/BUILD.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Projects deployed through ICP Ninja are temporary; they will only be live for 30
44

55
### 1. Install developer tools
66

7-
Install [Node.js](https://nodejs.org/en/download/) and [icp-cli](https://cli.icp.build):
7+
Install [Node.js](https://nodejs.org/en/download/) and [icp-cli](https://cli.internetcomputer.org):
88

99
```bash
1010
npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm

motoko/flying_ninja/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ This example can be deployed directly from [ICP Ninja](https://icp.ninja), a bro
1212

1313
[![Open in ICP Ninja](https://icp.ninja/assets/open.svg)](https://icp.ninja/i?g=https://github.com/dfinity/examples/motoko/flying_ninja)
1414

15-
> **Note:** ICP Ninja currently uses `dfx` under the hood, which is why this example includes a `dfx.json` configuration file. `dfx` is the legacy CLI, being superseded by [icp-cli](https://cli.icp.build), which is what developers should use for local development.
15+
> **Note:** ICP Ninja currently uses `dfx` under the hood, which is why this example includes a `dfx.json` configuration file. `dfx` is the legacy CLI, being superseded by [icp-cli](https://cli.internetcomputer.org), which is what developers should use for local development.
1616
1717
## Build and deploy from the command line
1818

1919
### Prerequisites
2020

2121
- [x] Install [Node.js](https://nodejs.org/en/download/)
22-
- [x] Install [icp-cli](https://cli.icp.build): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
22+
- [x] Install [icp-cli](https://cli.internetcomputer.org): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
2323

2424
### Install
2525

motoko/flying_ninja/backend/app.mo

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import Array "mo:base/Array";
2-
import Int "mo:base/Int";
3-
import Random "mo:base/Random";
1+
import Array "mo:core/Array";
2+
import Nat "mo:core/Nat";
3+
import Random "mo:core/Random";
44

55
persistent actor FlyingNinja {
6+
type Order = { #less; #equal; #greater };
67
type LeaderboardEntry = {
78
name : Text;
89
score : Nat;
@@ -25,16 +26,13 @@ persistent actor FlyingNinja {
2526
let newEntry : LeaderboardEntry = { name = name; score = score };
2627

2728
// Add the new entry and sort the leaderboard
28-
leaderboard := Array.sort<LeaderboardEntry>(
29-
Array.append<LeaderboardEntry>(leaderboard, [newEntry]),
30-
func(a : LeaderboardEntry, b : LeaderboardEntry) {
31-
Int.compare(b.score, a.score);
32-
}
29+
leaderboard := leaderboard.concat([newEntry]).sort(
30+
func(a : LeaderboardEntry, b : LeaderboardEntry) : Order { Nat.compare(b.score, a.score) }
3331
);
3432

3533
// Keep only the top 10 scores
3634
if (leaderboard.size() > 10) {
37-
leaderboard := Array.subArray(leaderboard, 0, 10);
35+
leaderboard := leaderboard.sliceToArray(0, 10);
3836
};
3937

4038
return leaderboard;

motoko/flying_ninja/mops.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
# Motoko dependencies (https://mops.one/)
2+
13
[toolchain]
2-
moc = "1.2.0"
4+
moc = "1.5.1"
35

46
[dependencies]
5-
base = "0.14.9"
7+
core = "2.4.0"
8+
9+
[moc]
10+
# M0236: use context dot notation (e.g. map.get(k) instead of Map.get(map, compare, k))
11+
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
12+
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
13+
args = ["-W", "M0236", "-W", "M0237", "-W", "M0223"]

0 commit comments

Comments
 (0)