Skip to content

Commit e0f24cb

Browse files
authored
chore: migrate defi examples to mo:core 2.4.0 (#1332)
Migrates basic_bitcoin, icp_transfer, token_transfer, and token_transfer_from from mo:base to mo:core 2.4.0 with modern Motoko style (persistent actor, context dot notation, await with cycles, transient let).
1 parent 8d5c6ec commit e0f24cb

21 files changed

Lines changed: 167 additions & 149 deletions

File tree

motoko/basic_bitcoin/mops.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1+
[toolchain]
2+
moc = "1.5.1"
3+
14
[dependencies]
2-
base = "0.11.2"
3-
core = "1.0.0"
5+
core = "2.4.0"
46
bitcoin = "0.1.0"
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/basic_bitcoin/src/basic_bitcoin/src/BitcoinApi.mo

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import ExperimentalCycles "mo:base/ExperimentalCycles";
2-
31
import Types "Types";
42

53
module {
@@ -36,8 +34,7 @@ module {
3634
/// Relies on the `bitcoin_get_balance` endpoint.
3735
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_balance
3836
public func get_balance(network : Network, address : BitcoinAddress) : async Satoshi {
39-
ExperimentalCycles.add<system>(GET_BALANCE_COST_CYCLES);
40-
await management_canister_actor.bitcoin_get_balance({
37+
await (with cycles = GET_BALANCE_COST_CYCLES) management_canister_actor.bitcoin_get_balance({
4138
address;
4239
network;
4340
min_confirmations = null;
@@ -49,8 +46,7 @@ module {
4946
/// NOTE: Relies on the `bitcoin_get_utxos` endpoint.
5047
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_utxos
5148
public func get_utxos(network : Network, address : BitcoinAddress) : async GetUtxosResponse {
52-
ExperimentalCycles.add<system>(GET_UTXOS_COST_CYCLES);
53-
await management_canister_actor.bitcoin_get_utxos({
49+
await (with cycles = GET_UTXOS_COST_CYCLES) management_canister_actor.bitcoin_get_utxos({
5450
address;
5551
network;
5652
filter = null;
@@ -63,8 +59,7 @@ module {
6359
/// Relies on the `bitcoin_get_current_fee_percentiles` endpoint.
6460
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_current_fee_percentiles
6561
public func get_current_fee_percentiles(network : Network) : async [MillisatoshiPerVByte] {
66-
ExperimentalCycles.add<system>(GET_CURRENT_FEE_PERCENTILES_COST_CYCLES);
67-
await management_canister_actor.bitcoin_get_current_fee_percentiles({
62+
await (with cycles = GET_CURRENT_FEE_PERCENTILES_COST_CYCLES) management_canister_actor.bitcoin_get_current_fee_percentiles({
6863
network;
6964
})
7065
};
@@ -74,11 +69,7 @@ module {
7469
/// Relies on the `bitcoin_send_transaction` endpoint.
7570
/// See https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_send_transaction
7671
public func send_transaction(network : Network, transaction : [Nat8]) : async () {
77-
let transaction_fee =
78-
SEND_TRANSACTION_BASE_COST_CYCLES + transaction.size() * SEND_TRANSACTION_COST_CYCLES_PER_BYTE;
79-
80-
ExperimentalCycles.add<system>(transaction_fee);
81-
await management_canister_actor.bitcoin_send_transaction({
72+
await (with cycles = SEND_TRANSACTION_BASE_COST_CYCLES + transaction.size() * SEND_TRANSACTION_COST_CYCLES_PER_BYTE) management_canister_actor.bitcoin_send_transaction({
8273
network;
8374
transaction;
8475
})

motoko/basic_bitcoin/src/basic_bitcoin/src/EcdsaApi.mo

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import ExperimentalCycles "mo:base/ExperimentalCycles";
2-
31
import Types "Types";
42

53
module {
@@ -30,8 +28,7 @@ module {
3028
};
3129

3230
public func sign_with_ecdsa(ecdsa_canister_actor: EcdsaCanisterActor, key_name : Text, derivation_path : [Blob], message_hash : Blob) : async Blob {
33-
ExperimentalCycles.add<system>(SIGN_WITH_ECDSA_COST_CYCLES);
34-
let res = await ecdsa_canister_actor.sign_with_ecdsa({
31+
let res = await (with cycles = SIGN_WITH_ECDSA_COST_CYCLES) ecdsa_canister_actor.sign_with_ecdsa({
3532
message_hash;
3633
derivation_path;
3734
key_id = {

motoko/basic_bitcoin/src/basic_bitcoin/src/Main.mo

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import Principal "mo:base/Principal";
2-
import Text "mo:base/Text";
3-
import Array "mo:base/Array";
4-
import Blob "mo:base/Blob";
1+
import Array "mo:core/Array";
2+
import Blob "mo:core/Blob";
53

64
import BitcoinApi "BitcoinApi";
75
import P2pkh "P2pkh";
@@ -43,8 +41,8 @@ persistent actor class BasicBitcoin(network : Types.Network) {
4341

4442
// Threshold signing APIs instantiated with the management canister ID. Can be
4543
// replaced for cheaper testing.
46-
transient var ecdsa_canister_actor : EcdsaCanisterActor = actor ("aaaaa-aa");
47-
transient var schnorr_canister_actor : SchnorrCanisterActor = actor ("aaaaa-aa");
44+
transient let ecdsa_canister_actor : EcdsaCanisterActor = actor ("aaaaa-aa");
45+
transient let schnorr_canister_actor : SchnorrCanisterActor = actor ("aaaaa-aa");
4846

4947
/// Returns the balance of the given Bitcoin address.
5048
public func get_balance(address : BitcoinAddress) : async Satoshi {
@@ -109,6 +107,6 @@ persistent actor class BasicBitcoin(network : Types.Network) {
109107
};
110108

111109
func derivationPathWithSuffix(suffix : Blob) : [[Nat8]] {
112-
Array.flatten([DERIVATION_PATH, [Blob.toArray(suffix)]]);
110+
[DERIVATION_PATH, [suffix.toArray()]].flatten();
113111
};
114112
};

motoko/basic_bitcoin/src/basic_bitcoin/src/P2pkh.mo

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
//! * Caching spent UTXOs so that they are not reused in future transactions.
99
//! * Option to set the fee.
1010

11-
import Debug "mo:base/Debug";
12-
import Array "mo:base/Array";
13-
import Nat8 "mo:base/Nat8";
14-
import Nat32 "mo:base/Nat32";
15-
import Nat64 "mo:base/Nat64";
16-
import Iter "mo:base/Iter";
17-
import Blob "mo:base/Blob";
18-
import Nat "mo:base/Nat";
11+
import Debug "mo:core/Debug";
12+
import Runtime "mo:core/Runtime";
13+
import Array "mo:core/Array";
14+
import Nat8 "mo:core/Nat8";
15+
import Nat32 "mo:core/Nat32";
16+
import Nat64 "mo:core/Nat64";
17+
import Blob "mo:core/Blob";
1918

2019
import EcdsaTypes "mo:bitcoin/ecdsa/Types";
2120
import P2pkh "mo:bitcoin/bitcoin/P2pkh";
@@ -50,10 +49,10 @@ module {
5049
/// Returns the P2PKH address of this canister at the given derivation path.
5150
public func get_address(ecdsa_canister_actor : EcdsaCanisterActor, network : Network, key_name : Text, derivation_path : [[Nat8]]) : async BitcoinAddress {
5251
// Fetch the public key of the given derivation path.
53-
let public_key = await EcdsaApi.ecdsa_public_key(ecdsa_canister_actor, key_name, Array.map(derivation_path, Blob.fromArray));
52+
let public_key = await EcdsaApi.ecdsa_public_key(ecdsa_canister_actor, key_name, derivation_path.map(Blob.fromArray));
5453

5554
// Compute the address.
56-
public_key_to_p2pkh_address(network, Blob.toArray(public_key));
55+
public_key_to_p2pkh_address(network, public_key.toArray());
5756
};
5857

5958
/// Sends a transaction to the network that transfers the given amount to the
@@ -74,7 +73,7 @@ module {
7473
};
7574

7675
// Fetch our public key, P2PKH address, and UTXOs.
77-
let own_public_key = Blob.toArray(await EcdsaApi.ecdsa_public_key(ecdsa_canister_actor, key_name, Array.map(derivation_path, Blob.fromArray)));
76+
let own_public_key = (await EcdsaApi.ecdsa_public_key(ecdsa_canister_actor, key_name, derivation_path.map(Blob.fromArray))).toArray();
7877
let own_address = public_key_to_p2pkh_address(network, own_public_key);
7978

8079
// Note that pagination may have to be used to get all UTXOs for the given address.
@@ -84,11 +83,11 @@ module {
8483

8584
// Build the transaction that sends `amount` to the destination address.
8685
let tx_bytes = await build_transaction(ecdsa_canister_actor, own_public_key, own_address, own_utxos, dst_address, amount, fee_per_vbyte);
87-
let transaction = Utils.get_ok(Transaction.fromBytes(Iter.fromArray(tx_bytes)));
86+
let transaction = Utils.get_ok(Transaction.fromBytes(tx_bytes.vals()));
8887

8988
// Sign the transaction.
90-
let signed_transaction_bytes = await sign_transaction(ecdsa_canister_actor, own_public_key, own_address, transaction, key_name, Array.map(derivation_path, Blob.fromArray), EcdsaApi.sign_with_ecdsa);
91-
let signed_transaction = Utils.get_ok(Transaction.fromBytes(Iter.fromArray(signed_transaction_bytes)));
89+
let signed_transaction_bytes = await sign_transaction(ecdsa_canister_actor, own_public_key, own_address, transaction, key_name, derivation_path.map(Blob.fromArray), EcdsaApi.sign_with_ecdsa);
90+
let signed_transaction = Utils.get_ok(Transaction.fromBytes(signed_transaction_bytes.vals()));
9291

9392
Debug.print("Sending transaction");
9493
await BitcoinApi.send_transaction(network, signed_transaction_bytes);
@@ -117,7 +116,7 @@ module {
117116
// We solve this problem iteratively. We start with a fee of zero, build
118117
// and sign a transaction, see what its size is, and then update the fee,
119118
// rebuild the transaction, until the fee is set to the correct amount.
120-
let fee_per_vbyte_nat = Nat64.toNat(fee_per_vbyte);
119+
let fee_per_vbyte_nat = fee_per_vbyte.toNat();
121120
Debug.print("Building transaction...");
122121
var total_fee : Nat = 0;
123122
loop {
@@ -166,27 +165,27 @@ module {
166165
// scriptPubKey of the Tx output being spent.
167166
switch (Address.scriptPubKey(#p2pkh own_address)) {
168167
case (#ok scriptPubKey) {
169-
let scriptSigs = Array.init<Script>(transaction.txInputs.size(), []);
168+
let scriptSigs = Array.repeat([] : Script, transaction.txInputs.size()).toVarArray<Script>();
170169

171170
// Obtain scriptSigs for each Tx input.
172-
for (i in Iter.range(0, transaction.txInputs.size() - 1)) {
171+
for (i in transaction.txInputs.keys()) {
173172
let sighash = transaction.createP2pkhSignatureHash(
174173
scriptPubKey,
175174
Nat32.fromIntWrap(i),
176175
SIGHASH_ALL,
177176
);
178177

179178
let signature_sec = await signer(ecdsa_canister_actor, key_name, derivation_path, Blob.fromArray(sighash));
180-
let signature_der = Blob.toArray(Der.encodeSignature(signature_sec));
179+
let signature_der = Der.encodeSignature(signature_sec).toArray();
181180

182181
// Append the sighash type.
183-
let encodedSignatureWithSighashType = Array.tabulate<Nat8>(
182+
let encodedSignatureWithSighashType = Array.tabulate(
184183
signature_der.size() + 1,
185-
func(n) {
184+
func(n : Nat) : Nat8 {
186185
if (n < signature_der.size()) {
187186
signature_der[n];
188187
} else {
189-
Nat8.fromNat(Nat32.toNat(SIGHASH_ALL));
188+
Nat8.fromNat(SIGHASH_ALL.toNat());
190189
};
191190
},
192191
);
@@ -200,12 +199,12 @@ module {
200199
scriptSigs[i] := script;
201200
};
202201
// Assign ScriptSigs to their associated TxInputs.
203-
for (i in Iter.range(0, scriptSigs.size() - 1)) {
202+
for (i in scriptSigs.keys()) {
204203
transaction.txInputs[i].script := scriptSigs[i];
205204
};
206205
};
207206
// Verify that our own address is P2PKH.
208-
case (#err msg) Debug.trap("This example supports signing p2pkh addresses only: " # msg);
207+
case (#err msg) Runtime.trap("This example supports signing p2pkh addresses only: " # msg);
209208
};
210209

211210
transaction.toBytes();

0 commit comments

Comments
 (0)