Ton
199 строк · 6.7 Кб
1;; Wallet smart contract with plugins
2
3#include "stdlib.fc";
4
5(slice, int) dict_get?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTGET" "NULLSWAPIFNOT";
6(cell, int) dict_add_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTADDB";
7(cell, int) dict_delete?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTDEL";
8
9() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
10var cs = in_msg_cell.begin_parse();
11var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
12if (flags & 1) {
13;; ignore all bounced messages
14return ();
15}
16if (in_msg.slice_bits() < 32) {
17;; ignore simple transfers
18return ();
19}
20int op = in_msg~load_uint(32);
21if (op != 0x706c7567) & (op != 0x64737472) { ;; "plug" & "dstr"
22;; ignore all messages not related to plugins
23return ();
24}
25slice s_addr = cs~load_msg_addr();
26(int wc, int addr_hash) = parse_std_addr(s_addr);
27slice wc_n_address = begin_cell().store_int(wc, 8).store_uint(addr_hash, 256).end_cell().begin_parse();
28var ds = get_data().begin_parse().skip_bits(32 + 32 + 256);
29var plugins = ds~load_dict();
30var (_, success?) = plugins.dict_get?(8 + 256, wc_n_address);
31if ~(success?) {
32;; it may be a transfer
33return ();
34}
35int query_id = in_msg~load_uint(64);
36var msg = begin_cell();
37if (op == 0x706c7567) { ;; request funds
38
39(int r_toncoins, cell r_extra) = (in_msg~load_grams(), in_msg~load_dict());
40
41[int my_balance, _] = get_balance();
42throw_unless(80, my_balance - msg_value >= r_toncoins);
43
44msg = msg.store_uint(0x18, 6)
45.store_slice(s_addr)
46.store_grams(r_toncoins)
47.store_dict(r_extra)
48.store_uint(0, 4 + 4 + 64 + 32 + 1 + 1)
49.store_uint(0x706c7567 | 0x80000000, 32)
50.store_uint(query_id, 64);
51send_raw_message(msg.end_cell(), 64);
52
53}
54
55if (op == 0x64737472) { ;; remove plugin by its request
56
57plugins~dict_delete?(8 + 256, wc_n_address);
58var ds = get_data().begin_parse().first_bits(32 + 32 + 256);
59set_data(begin_cell().store_slice(ds).store_dict(plugins).end_cell());
60;; return coins only if bounce expected
61if (flags & 2) {
62msg = msg.store_uint(0x18, 6)
63.store_slice(s_addr)
64.store_grams(0)
65.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
66.store_uint(0x64737472 | 0x80000000, 32)
67.store_uint(query_id, 64);
68send_raw_message(msg.end_cell(), 64);
69}
70}
71}
72
73() recv_external(slice in_msg) impure {
74var signature = in_msg~load_bits(512);
75var cs = in_msg;
76var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
77throw_if(36, valid_until <= now());
78var ds = get_data().begin_parse();
79var (stored_seqno, stored_subwallet, public_key, plugins) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256), ds~load_dict());
80ds.end_parse();
81throw_unless(33, msg_seqno == stored_seqno);
82throw_unless(34, subwallet_id == stored_subwallet);
83throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
84accept_message();
85set_data(begin_cell()
86.store_uint(stored_seqno + 1, 32)
87.store_uint(stored_subwallet, 32)
88.store_uint(public_key, 256)
89.store_dict(plugins)
90.end_cell());
91commit();
92cs~touch();
93int op = cs~load_uint(8);
94
95if (op == 0) { ;; simple send
96while (cs.slice_refs()) {
97var mode = cs~load_uint(8);
98send_raw_message(cs~load_ref(), mode);
99}
100return (); ;; have already saved the storage
101}
102
103if (op == 1) { ;; deploy and install plugin
104int plugin_workchain = cs~load_int(8);
105int plugin_balance = cs~load_grams();
106(cell state_init, cell body) = (cs~load_ref(), cs~load_ref());
107int plugin_address = cell_hash(state_init);
108slice wc_n_address = begin_cell().store_int(plugin_workchain, 8).store_uint(plugin_address, 256).end_cell().begin_parse();
109var msg = begin_cell()
110.store_uint(0x18, 6)
111.store_uint(4, 3).store_slice(wc_n_address)
112.store_grams(plugin_balance)
113.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
114.store_ref(state_init)
115.store_ref(body);
116send_raw_message(msg.end_cell(), 3);
117(plugins, int success?) = plugins.dict_add_builder?(8 + 256, wc_n_address, begin_cell());
118throw_unless(39, success?);
119}
120
121if (op == 2) { ;; install plugin
122slice wc_n_address = cs~load_bits(8 + 256);
123int amount = cs~load_grams();
124int query_id = cs~load_uint(64);
125
126(plugins, int success?) = plugins.dict_add_builder?(8 + 256, wc_n_address, begin_cell());
127throw_unless(39, success?);
128
129builder msg = begin_cell()
130.store_uint(0x18, 6)
131.store_uint(4, 3).store_slice(wc_n_address)
132.store_grams(amount)
133.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
134.store_uint(0x6e6f7465, 32) ;; op
135.store_uint(query_id, 64);
136send_raw_message(msg.end_cell(), 3);
137}
138
139if (op == 3) { ;; remove plugin
140slice wc_n_address = cs~load_bits(8 + 256);
141int amount = cs~load_grams();
142int query_id = cs~load_uint(64);
143
144(plugins, int success?) = plugins.dict_delete?(8 + 256, wc_n_address);
145throw_unless(39, success?);
146
147builder msg = begin_cell()
148.store_uint(0x18, 6)
149.store_uint(4, 3).store_slice(wc_n_address)
150.store_grams(amount)
151.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
152.store_uint(0x64737472, 32) ;; op
153.store_uint(query_id, 64);
154send_raw_message(msg.end_cell(), 3);
155}
156
157set_data(begin_cell()
158.store_uint(stored_seqno + 1, 32)
159.store_uint(stored_subwallet, 32)
160.store_uint(public_key, 256)
161.store_dict(plugins)
162.end_cell());
163}
164
165;; Get methods
166
167int seqno() method_id {
168return get_data().begin_parse().preload_uint(32);
169}
170
171int get_subwallet_id() method_id {
172return get_data().begin_parse().skip_bits(32).preload_uint(32);
173}
174
175int get_public_key() method_id {
176var cs = get_data().begin_parse().skip_bits(64);
177return cs.preload_uint(256);
178}
179
180int is_plugin_installed(int wc, int addr_hash) method_id {
181var ds = get_data().begin_parse().skip_bits(32 + 32 + 256);
182var plugins = ds~load_dict();
183var (_, success?) = plugins.dict_get?(8 + 256, begin_cell().store_int(wc, 8).store_uint(addr_hash, 256).end_cell().begin_parse());
184return success?;
185}
186
187tuple get_plugin_list() method_id {
188var list = null();
189var ds = get_data().begin_parse().skip_bits(32 + 32 + 256);
190var plugins = ds~load_dict();
191do {
192var (wc_n_address, _, f) = plugins~dict::delete_get_min(8 + 256);
193if (f) {
194(int wc, int addr) = (wc_n_address~load_int(8), wc_n_address~load_uint(256));
195list = cons(pair(wc, addr), list);
196}
197} until (~ f);
198return list;
199}
200