Skip to content

Commit 229c6e7

Browse files
authored
chore(motoko): migrate @SDK examples to mo:core 2.4.0 (#1330)
* chore(motoko): migrate @SDK examples to mo:core 2.4.0 - canister_factory: bump moc/core in mops.toml (already used mo:core) - query_stats: swap 3 mo:base imports to mo:core - pub-sub: create mops.toml; replace mo:base/List with mo:core/Array - ic-pos: create mops.toml; migrate Trie→mo:core/Map, Buffer→Array, ExperimentalCycles→await-with-cycles - icrc2-swap: create mops.toml; migrate TrieMap→mo:core/Map, remove preupgrade/postupgrade (Map is stable by default) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fixup: apply context dot and implicit compare throughout sdk examples Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fixup: fix mops check warnings in pub-sub and icrc2-swap - pub-sub: add explicit `: async ()` return types (M0242) - icrc2-swap: remove unused `msg` binding in deposit/swap (M0240); consolidate double Nat subtraction into single deduction var (M0155) * fix: use oneway `: ()` return type for pub-sub subscribe/publish `: async ()` caused a type error in sub/Main.mo which calls Publisher.subscribe without await. The correct M0242 fix is explicit oneway annotation since fire-and-forget is intentional here. * chore: replace [compiler] with [moc] warning flags in sdk mops.toml [compiler] warnings = "error" is a mops-only setting not read by dfx in CI. [moc] args = ["-W", "M0236", ...] passes flags directly to moc and is effective in both mops check and dfx builds. * chore: use compact -W=M0236,M0237,M0223 format in mops.toml
1 parent 82ef873 commit 229c6e7

9 files changed

Lines changed: 135 additions & 145 deletions

File tree

motoko/canister_factory/mops.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
[toolchain]
2+
moc = "1.5.1"
3+
14
[dependencies]
2-
core = "1.0.0" # Check the latest version: https://mops.one/core
5+
core = "2.4.0"
6+
37

8+
[moc]
9+
# M0236: use context dot notation (e.g. x.toText() instead of Nat.toText(x))
10+
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
11+
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
12+
args = ["-W=M0236,M0237,M0223"]

motoko/ic-pos/mops.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[toolchain]
2+
moc = "1.5.1"
3+
4+
[dependencies]
5+
core = "2.4.0"
6+
7+
8+
[moc]
9+
# M0236: use context dot notation (e.g. x.toText() instead of Nat.toText(x))
10+
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
11+
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
12+
args = ["-W=M0236,M0237,M0223"]

motoko/ic-pos/src/icpos/main.mo

Lines changed: 30 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
// Importing base modules
2-
import Array "mo:base/Array";
3-
import Blob "mo:base/Blob";
4-
import Cycles "mo:base/ExperimentalCycles";
5-
import Debug "mo:base/Debug";
6-
import Nat "mo:base/Nat";
7-
import Nat64 "mo:base/Nat64";
8-
import Principal "mo:base/Principal";
9-
import Text "mo:base/Text";
10-
import Time "mo:base/Time";
11-
import Trie "mo:base/Trie";
12-
import Buffer "mo:base/Buffer";
13-
14-
// Importing local modules
1+
import Array "mo:core/Array";
2+
import Blob "mo:core/Blob";
3+
import Debug "mo:core/Debug";
4+
import Map "mo:core/Map";
5+
import Nat "mo:core/Nat";
6+
import Nat64 "mo:core/Nat64";
7+
import Principal "mo:core/Principal";
8+
import Text "mo:core/Text";
9+
import Time "mo:core/Time";
10+
1511
import MainTypes "main.types";
1612
import CkBtcLedger "canister:icrc1_ledger";
1713
import HttpTypes "http/http.types";
@@ -26,18 +22,18 @@ import HttpTypes "http/http.types";
2622
*/
2723
shared (actorContext) persistent actor class Main(_startBlock : Nat) {
2824

29-
private var merchantStore : Trie.Trie<Text, MainTypes.Merchant> = Trie.empty();
25+
private let merchantStore = Map.empty<Text, MainTypes.Merchant>();
3026
private var latestTransactionIndex : Nat = 0;
3127
private var courierApiKey : Text = "";
32-
private transient var logData = Buffer.Buffer<Text>(0);
28+
private transient var logData : [Text] = [];
3329

3430
/**
3531
* Get the merchant's information
3632
*/
3733
public query (context) func getMerchant() : async MainTypes.Response<MainTypes.Merchant> {
3834
let caller : Principal = context.caller;
3935

40-
switch (Trie.get(merchantStore, merchantKey(Principal.toText(caller)), Text.equal)) {
36+
switch (merchantStore.get(caller.toText())) {
4137
case (?merchant) {
4238
{
4339
status = 200;
@@ -51,7 +47,7 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
5147
status = 404;
5248
status_text = "Not Found";
5349
data = null;
54-
error_text = ?("Merchant with principal ID: " # Principal.toText(caller) # " not found.");
50+
error_text = ?("Merchant with principal ID: " # caller.toText() # " not found.");
5551
};
5652
};
5753
};
@@ -61,14 +57,8 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
6157
* Update the merchant's information
6258
*/
6359
public shared (context) func updateMerchant(merchant : MainTypes.Merchant) : async MainTypes.Response<MainTypes.Merchant> {
64-
6560
let caller : Principal = context.caller;
66-
merchantStore := Trie.replace(
67-
merchantStore,
68-
merchantKey(Principal.toText(caller)),
69-
Text.equal,
70-
?merchant,
71-
).0;
61+
let _ = merchantStore.swap(caller.toText(), merchant);
7262
{
7363
status = 200;
7464
status_text = "OK";
@@ -81,7 +71,7 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
8171
* Set the courier API key. Only the owner can set the courier API key.
8272
*/
8373
public shared (context) func setCourierApiKey(apiKey : Text) : async MainTypes.Response<Text> {
84-
if (not Principal.equal(context.caller, actorContext.caller)) {
74+
if (context.caller != actorContext.caller) {
8575
return {
8676
status = 403;
8777
status_text = "Forbidden";
@@ -102,28 +92,17 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
10292
* Get latest log items. Log output is capped at 100 items.
10393
*/
10494
public query func getLogs() : async [Text] {
105-
Buffer.toArray(logData);
95+
logData;
10696
};
10797

10898
/**
10999
* Log a message. Log output is capped at 100 items.
110100
*/
111101
private func log(text : Text) {
112102
Debug.print(text);
113-
logData.reserve(logData.size() + 1);
114-
logData.insert(0, text);
115-
// Cap the log at 100 items
116-
if (logData.size() == 100) {
117-
let _ = logData.removeLast();
118-
};
119-
return;
120-
};
121-
122-
/**
123-
* Generate a Trie key based on a merchant's principal ID
124-
*/
125-
private func merchantKey(x : Text) : Trie.Key<Text> {
126-
return { hash = Text.hash(x); key = x };
103+
logData := Array.tabulate<Text>((logData.size() + 1).min(100), func(i : Nat) : Text {
104+
if (i == 0) text else logData[i - 1]
105+
});
127106
};
128107

129108
/**
@@ -150,15 +129,15 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
150129
length = 1;
151130
});
152131

153-
if (Array.size(response.transactions) > 0) {
132+
if (response.transactions.size() > 0) {
154133
latestTransactionIndex := start;
155134

156135
if (response.transactions[0].kind == "transfer") {
157136
let t = response.transactions[0];
158137
switch (t.transfer) {
159138
case (?transfer) {
160139
let to = transfer.to.owner;
161-
switch (Trie.get(merchantStore, merchantKey(Principal.toText(to)), Text.equal)) {
140+
switch (merchantStore.get(to.toText())) {
162141
case (?merchant) {
163142
if (merchant.email_notifications or merchant.phone_notifications) {
164143
log("Sending notification to: " # debug_show (merchant.email_address));
@@ -182,28 +161,23 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
182161
* Send a notification to a merchant about a received payment
183162
*/
184163
private func sendNotification(merchant : MainTypes.Merchant, transaction : CkBtcLedger.Transaction) : async () {
185-
// Managment canister
186164
let ic : HttpTypes.IC = actor ("aaaaa-aa");
187165

188-
// Create request body
189166
var amount = "0";
190167
var from = "";
191168
switch (transaction.transfer) {
192169
case (?transfer) {
193-
amount := Nat.toText(transfer.amount);
194-
from := Principal.toText(transfer.from.owner);
170+
amount := transfer.amount.toText();
171+
from := transfer.from.owner.toText();
195172
};
196173
case null {};
197174
};
198-
let idempotencyKey : Text = Text.concat(merchant.name, Nat64.toText(transaction.timestamp));
175+
let idempotencyKey : Text = merchant.name # transaction.timestamp.toText();
199176
let requestBodyJson : Text = "{ \"idempotencyKey\": \"" # idempotencyKey # "\", \"email\": \"" # merchant.email_address # "\", \"phone\": \"" # merchant.phone_number # "\", \"amount\": \"" # amount # "\", \"payer\": \"" # from # "\"}";
200-
let requestBodyAsBlob : Blob = Text.encodeUtf8(requestBodyJson);
201-
let requestBodyAsNat8 : [Nat8] = Blob.toArray(requestBodyAsBlob);
177+
let requestBodyAsBlob : Blob = requestBodyJson.encodeUtf8();
178+
let requestBodyAsNat8 : [Nat8] = requestBodyAsBlob.toArray();
202179

203-
// Setup request
204180
let httpRequest : HttpTypes.HttpRequestArgs = {
205-
// The notification service is hosted on Netlify and the URL is hardcoded
206-
// in this example. In a real application, the URL would be configurable.
207181
url = "https://icpos-notifications.xyz/.netlify/functions/notify";
208182
max_response_bytes = ?Nat64.fromNat(1000);
209183
headers = [
@@ -217,15 +191,11 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
217191
// Cycle cost of sending a notification
218192
// 49.14M + 5200 * request_size + 10400 * max_response_bytes
219193
// 49.14M + (5200 * 1000) + (10400 * 1000) = 64.74M
220-
Cycles.add<system>(70_000_000);
221-
222-
// Send the request
223-
let httpResponse : HttpTypes.HttpResponsePayload = await ic.http_request(httpRequest);
194+
let httpResponse : HttpTypes.HttpResponsePayload = await (with cycles = 70_000_000) ic.http_request(httpRequest);
224195

225-
// Check the response
226196
if (httpResponse.status > 299) {
227-
let response_body : Blob = Blob.fromArray(httpResponse.body);
228-
let decoded_text : Text = switch (Text.decodeUtf8(response_body)) {
197+
let response_body : Blob = httpResponse.body.fromArray();
198+
let decoded_text : Text = switch (response_body.decodeUtf8()) {
229199
case (null) { "No value returned" };
230200
case (?y) { y };
231201
};

motoko/icrc2-swap/mops.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[toolchain]
2+
moc = "1.5.1"
3+
4+
[dependencies]
5+
core = "2.4.0"
6+
7+
8+
[moc]
9+
# M0236: use context dot notation (e.g. x.toText() instead of Nat.toText(x))
10+
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
11+
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
12+
args = ["-W=M0236,M0237,M0223"]

0 commit comments

Comments
 (0)