Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion motoko/canister_factory/mops.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
[toolchain]
moc = "1.5.1"

[dependencies]
core = "1.0.0" # Check the latest version: https://mops.one/core
core = "2.4.0"


[moc]
# M0236: use context dot notation (e.g. x.toText() instead of Nat.toText(x))
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
args = ["-W=M0236,M0237,M0223"]
12 changes: 12 additions & 0 deletions motoko/ic-pos/mops.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[toolchain]
moc = "1.5.1"

[dependencies]
core = "2.4.0"


[moc]
# M0236: use context dot notation (e.g. x.toText() instead of Nat.toText(x))
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
args = ["-W=M0236,M0237,M0223"]
90 changes: 30 additions & 60 deletions motoko/ic-pos/src/icpos/main.mo
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
// Importing base modules
import Array "mo:base/Array";
import Blob "mo:base/Blob";
import Cycles "mo:base/ExperimentalCycles";
import Debug "mo:base/Debug";
import Nat "mo:base/Nat";
import Nat64 "mo:base/Nat64";
import Principal "mo:base/Principal";
import Text "mo:base/Text";
import Time "mo:base/Time";
import Trie "mo:base/Trie";
import Buffer "mo:base/Buffer";

// Importing local modules
import Array "mo:core/Array";
import Blob "mo:core/Blob";
import Debug "mo:core/Debug";
import Map "mo:core/Map";
import Nat "mo:core/Nat";
import Nat64 "mo:core/Nat64";
import Principal "mo:core/Principal";
import Text "mo:core/Text";
import Time "mo:core/Time";

import MainTypes "main.types";
import CkBtcLedger "canister:icrc1_ledger";
import HttpTypes "http/http.types";
Expand All @@ -26,18 +22,18 @@ import HttpTypes "http/http.types";
*/
shared (actorContext) persistent actor class Main(_startBlock : Nat) {

private var merchantStore : Trie.Trie<Text, MainTypes.Merchant> = Trie.empty();
private let merchantStore = Map.empty<Text, MainTypes.Merchant>();
private var latestTransactionIndex : Nat = 0;
private var courierApiKey : Text = "";
private transient var logData = Buffer.Buffer<Text>(0);
private transient var logData : [Text] = [];

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

switch (Trie.get(merchantStore, merchantKey(Principal.toText(caller)), Text.equal)) {
switch (merchantStore.get(caller.toText())) {
case (?merchant) {
{
status = 200;
Expand All @@ -51,7 +47,7 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
status = 404;
status_text = "Not Found";
data = null;
error_text = ?("Merchant with principal ID: " # Principal.toText(caller) # " not found.");
error_text = ?("Merchant with principal ID: " # caller.toText() # " not found.");
};
};
};
Expand All @@ -61,14 +57,8 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
* Update the merchant's information
*/
public shared (context) func updateMerchant(merchant : MainTypes.Merchant) : async MainTypes.Response<MainTypes.Merchant> {

let caller : Principal = context.caller;
merchantStore := Trie.replace(
merchantStore,
merchantKey(Principal.toText(caller)),
Text.equal,
?merchant,
).0;
let _ = merchantStore.swap(caller.toText(), merchant);
{
status = 200;
status_text = "OK";
Expand All @@ -81,7 +71,7 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
* Set the courier API key. Only the owner can set the courier API key.
*/
public shared (context) func setCourierApiKey(apiKey : Text) : async MainTypes.Response<Text> {
if (not Principal.equal(context.caller, actorContext.caller)) {
if (context.caller != actorContext.caller) {
return {
status = 403;
status_text = "Forbidden";
Expand All @@ -102,28 +92,17 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
* Get latest log items. Log output is capped at 100 items.
*/
public query func getLogs() : async [Text] {
Buffer.toArray(logData);
logData;
};

/**
* Log a message. Log output is capped at 100 items.
*/
private func log(text : Text) {
Debug.print(text);
logData.reserve(logData.size() + 1);
logData.insert(0, text);
// Cap the log at 100 items
if (logData.size() == 100) {
let _ = logData.removeLast();
};
return;
};

/**
* Generate a Trie key based on a merchant's principal ID
*/
private func merchantKey(x : Text) : Trie.Key<Text> {
return { hash = Text.hash(x); key = x };
logData := Array.tabulate<Text>((logData.size() + 1).min(100), func(i : Nat) : Text {
if (i == 0) text else logData[i - 1]
});
};

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

if (Array.size(response.transactions) > 0) {
if (response.transactions.size() > 0) {
latestTransactionIndex := start;

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

// Create request body
var amount = "0";
var from = "";
switch (transaction.transfer) {
case (?transfer) {
amount := Nat.toText(transfer.amount);
from := Principal.toText(transfer.from.owner);
amount := transfer.amount.toText();
from := transfer.from.owner.toText();
};
case null {};
};
let idempotencyKey : Text = Text.concat(merchant.name, Nat64.toText(transaction.timestamp));
let idempotencyKey : Text = merchant.name # transaction.timestamp.toText();
let requestBodyJson : Text = "{ \"idempotencyKey\": \"" # idempotencyKey # "\", \"email\": \"" # merchant.email_address # "\", \"phone\": \"" # merchant.phone_number # "\", \"amount\": \"" # amount # "\", \"payer\": \"" # from # "\"}";
let requestBodyAsBlob : Blob = Text.encodeUtf8(requestBodyJson);
let requestBodyAsNat8 : [Nat8] = Blob.toArray(requestBodyAsBlob);
let requestBodyAsBlob : Blob = requestBodyJson.encodeUtf8();
let requestBodyAsNat8 : [Nat8] = requestBodyAsBlob.toArray();

// Setup request
let httpRequest : HttpTypes.HttpRequestArgs = {
// The notification service is hosted on Netlify and the URL is hardcoded
// in this example. In a real application, the URL would be configurable.
url = "https://icpos-notifications.xyz/.netlify/functions/notify";
max_response_bytes = ?Nat64.fromNat(1000);
headers = [
Expand All @@ -217,15 +191,11 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
// Cycle cost of sending a notification
// 49.14M + 5200 * request_size + 10400 * max_response_bytes
// 49.14M + (5200 * 1000) + (10400 * 1000) = 64.74M
Cycles.add<system>(70_000_000);

// Send the request
let httpResponse : HttpTypes.HttpResponsePayload = await ic.http_request(httpRequest);
let httpResponse : HttpTypes.HttpResponsePayload = await (with cycles = 70_000_000) ic.http_request(httpRequest);

// Check the response
if (httpResponse.status > 299) {
let response_body : Blob = Blob.fromArray(httpResponse.body);
let decoded_text : Text = switch (Text.decodeUtf8(response_body)) {
let response_body : Blob = httpResponse.body.fromArray();
let decoded_text : Text = switch (response_body.decodeUtf8()) {
case (null) { "No value returned" };
case (?y) { y };
};
Expand Down
12 changes: 12 additions & 0 deletions motoko/icrc2-swap/mops.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[toolchain]
moc = "1.5.1"

[dependencies]
core = "2.4.0"


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