-
Notifications
You must be signed in to change notification settings - Fork 3
/
main.mo
181 lines (159 loc) · 6.81 KB
/
main.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import Array "mo:base/Array";
import Blob "mo:base/Blob";
import Cycles "mo:base/ExperimentalCycles";
import Error "mo:base/Error";
import Nat "mo:base/Nat";
import Option "mo:base/Option";
import Principal "mo:base/Principal";
import Text "mo:base/Text";
import Time "mo:base/Time";
import Trie "mo:base/Trie";
import Account "./account";
import Escrow "./escrow";
import Hex "./hex";
import Types "./types";
import Utils "./utils";
actor EscrowManager {
let admins : [Principal] = [
Principal.fromText("sqvf2-x3s5d-3a5m3-czni6-2zuda-rg4bk-7le4o-md733-uc75o-yihay-4ae"),
Principal.fromText("imptf-t7jg2-g5akw-v4vai-u4qiy-ayepe-7a3vi-tf4hu-pe37j-iqcis-hqe"),
Principal.fromText("krpk7-5knvf-l2jy3-mfzbr-6arys-xhrc5-5k76j-hfszl-mlm37-pqsd2-eqe"),
Principal.fromText("is7gy-jgfpp-4fnpe-da4au-xbb5e-iflz6-kuqge-wef4p-fpeo4-gftlc-mae")
];
type canister_id = Principal;
type user_id = Principal;
type wasm_module = Blob;
type canister_settings = {
controllers : ?[Principal];
compute_allocation : ?Nat;
memory_allocation : ?Nat;
freezing_threshold : ?Nat;
};
type definite_canister_settings = {
controllers : ?[Principal];
compute_allocation : Nat;
memory_allocation : Nat;
freezing_threshold : Nat;
};
// We want to eventually make all escrow canister "black hole" canisters,
// which means they have no controllers, and hence their code cannot be
// altered. Until then, since we have at times had to update an escrow
// canister's code due to a glitch, we use this function to temporarily take
// control of the escrow canister.
// TODO: Remove this function.
public func takeover (canister : Text) : async definite_canister_settings {
let ManagementCanister = actor "aaaaa-aa" : actor {
canister_status : shared { canister_id : canister_id } -> async {
status : { #running; #stopping; #stopped };
settings: definite_canister_settings;
module_hash: ?Blob;
memory_size: Nat;
cycles: Nat;
};
update_settings : shared {
canister_id : Principal;
settings : canister_settings
} -> async ();
};
let canister_id = Principal.fromText(canister);
let newControllers = [
Principal.fromText("3fhg4-qiaaa-aaaak-aajiq-cai"),
Principal.fromText("xohn2-daaaa-aaaak-aadvq-cai"),
Principal.fromText("is7gy-jgfpp-4fnpe-da4au-xbb5e-iflz6-kuqge-wef4p-fpeo4-gftlc-mae"),
];
await ManagementCanister.update_settings({ canister_id = canister_id; settings = {
controllers = ?newControllers;
compute_allocation = ?(0 : Nat);
memory_allocation = ?(0 : Nat);
freezing_threshold = ?(2_592_000 : Nat);
}});
return (await ManagementCanister.canister_status({ canister_id = canister_id })).settings;
};
type AccountIdText = Types.AccountIdText;
type CanisterId = Principal;
type CanisterIdText = Text;
type NFTInfo = Types.NFTInfo;
type ProjectId = Types.ProjectId;
type ProjectIdText = Text;
type SubaccountBlob = Types.SubaccountBlob;
stable var escrowCanisters : Trie.Trie<ProjectIdText, CanisterId> = Trie.empty();
stable var maxOversellPercentage : Nat = 20;
// Canister management
public query func getProjectEscrowCanisterPrincipal(p: ProjectId) : async ?CanisterIdText {
switch (getProjectEscrowCanister(p)) {
case (?canister) ?Principal.toText(canister);
case (null) null;
};
};
// TODO: Remove self as controller of created escrow canister to turn the canister into true "black hole" canister.
public shared(msg) func createEscrowCanister (
p: ProjectId,
recipientICP: Principal,
recipientBTC: Text,
nfts: [NFTInfo],
endTime : Time.Time,
maxNFTsPerWallet : Nat,
network : Types.Network,
backendPrincipal: Text,
oversellPercentage: Nat
) : async () {
assert(isAdmin(msg.caller));
switch (getProjectEscrowCanister(p)) {
case (?canister) { throw Error.reject("Project already has an escrow canister: " # Principal.toText(canister)); };
case (null) {
if (oversellPercentage > maxOversellPercentage) {
throw Error.reject("Oversell percentage can't exceed " # Nat.toText(maxOversellPercentage) # "%");
} else {
Cycles.add(1000000000000);
let canister = await Escrow.EscrowCanister(p, recipientICP, recipientBTC, nfts, endTime, maxNFTsPerWallet, network, backendPrincipal, oversellPercentage);
escrowCanisters := Trie.putFresh<ProjectIdText, CanisterId>(escrowCanisters, projectIdKey(p), Text.equal, Principal.fromActor(canister));
};
};
};
};
public shared({caller}) func setMaxOversellPercentage(percentage: Nat): async () {
assert(isAdmin(caller));
maxOversellPercentage := percentage;
};
public query func getMaxOversellPercentage(): async Nat {
maxOversellPercentage;
};
func getProjectEscrowCanister (p: ProjectId) : ?CanisterId {
Trie.get<ProjectIdText, CanisterId>(escrowCanisters, projectIdKey(p), Text.equal);
};
public shared(msg) func dissociateEscrowCanister (p: ProjectId) : async () {
assert(isAdmin(msg.caller));
switch (getProjectEscrowCanister(p)) {
case (?canister) {
escrowCanisters := Trie.remove<ProjectIdText, CanisterId>(escrowCanisters, projectIdKey(p), Text.equal).0;
};
case (null) { throw Error.reject("Project has no escrow canister"); };
};
};
// helpers
func projectIdKey (p: ProjectId) : Trie.Key<ProjectIdText> {
{ key = Nat.toText(p); hash = Text.hash(Nat.toText(p)) };
};
// cycles management
//Internal cycle management - good general case
type RecieveOptions = {
memo: ?Text;
};
public func wallet_receive() : async () {
let available = Cycles.available();
let accepted = Cycles.accept(available);
assert (accepted == available);
};
public func acceptCycles() : async () {
let available = Cycles.available();
let accepted = Cycles.accept(available);
assert (accepted == available);
};
public query func availableCycles() : async Nat {
return Cycles.balance();
};
private func isAdmin(p: Principal): Bool {
func identity(x: Principal): Bool { x == p };
Option.isSome(Array.find<Principal>(admins,identity));
};
}