Ton

Форк
0
250 строк · 9.5 Кб
1
;; Jetton Wallet Smart Contract
2

3
#include "imports/stdlib.fc";
4
#include "imports/params.fc";
5
#include "imports/constants.fc";
6
#include "imports/jetton-utils.fc";
7
#include "imports/op-codes.fc";
8
#include "imports/utils.fc";
9
#pragma version >=0.2.0;
10

11
{-
12

13
NOTE that this tokens can be transferred within the same workchain.
14

15
This is suitable for most tokens, if you need tokens transferable between workchains there are two solutions:
16

17
1) use more expensive but universal function to calculate message forward fee for arbitrary destination (see `misc/forward-fee-calc.cs`)
18

19
2) use token holder proxies in target workchain (that way even 'non-universal' token can be used from any workchain)
20

21
-}
22

23
const min_tons_for_storage = 10000000; ;; 0.01 TON
24
const gas_consumption = 10000000; ;; 0.01 TON
25

26
{-
27
  Storage
28
  storage#_ balance:Coins owner_address:MsgAddressInt jetton_master_address:MsgAddressInt jetton_wallet_code:^Cell = Storage;
29
-}
30

31
(int, slice, slice, cell) load_data() inline {
32
  slice ds = get_data().begin_parse();
33
  return (ds~load_coins(), ds~load_msg_addr(), ds~load_msg_addr(), ds~load_ref());
34
}
35

36
() save_data (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) impure inline {
37
  set_data(pack_jetton_wallet_data(balance, owner_address, jetton_master_address, jetton_wallet_code));
38
}
39

40
{-
41
  transfer query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress
42
           response_destination:MsgAddress custom_payload:(Maybe ^Cell)
43
           forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell)
44
           = InternalMsgBody;
45
  internal_transfer  query_id:uint64 amount:(VarUInteger 16) from:MsgAddress
46
                     response_address:MsgAddress
47
                     forward_ton_amount:(VarUInteger 16)
48
                     forward_payload:(Either Cell ^Cell) 
49
                     = InternalMsgBody;
50
-}
51

52
() send_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure {
53
  int query_id = in_msg_body~load_uint(64);
54
  int jetton_amount = in_msg_body~load_coins();
55
  slice to_owner_address = in_msg_body~load_msg_addr();
56
  force_chain(to_owner_address);
57
  (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
58
  balance -= jetton_amount;
59

60
  throw_unless(705, equal_slices(owner_address, sender_address));
61
  throw_unless(706, balance >= 0);
62

63
  cell state_init = calculate_jetton_wallet_state_init(to_owner_address, jetton_master_address, jetton_wallet_code);
64
  slice to_wallet_address = calculate_jetton_wallet_address(state_init);
65
  slice response_address = in_msg_body~load_msg_addr();
66
  cell custom_payload = in_msg_body~load_dict();
67
  int forward_ton_amount = in_msg_body~load_coins();
68
  throw_unless(708, slice_bits(in_msg_body) >= 1);
69
  slice either_forward_payload = in_msg_body;
70
  var msg = begin_cell()
71
    .store_uint(0x18, 6)
72
    .store_slice(to_wallet_address)
73
    .store_coins(0)
74
    .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
75
    .store_ref(state_init);
76
  var msg_body = begin_cell()
77
    .store_uint(op::internal_transfer(), 32)
78
    .store_uint(query_id, 64)
79
    .store_coins(jetton_amount)
80
    .store_slice(owner_address)
81
    .store_slice(response_address)
82
    .store_coins(forward_ton_amount)
83
    .store_slice(either_forward_payload)
84
    .end_cell();
85

86
  msg = msg.store_ref(msg_body);
87
  int fwd_count = forward_ton_amount ? 2 : 1;
88
  throw_unless(709, msg_value >
89
                     forward_ton_amount +
90
                     ;; 3 messages: wal1->wal2,  wal2->owner, wal2->response
91
                     ;; but last one is optional (it is ok if it fails)
92
                     fwd_count * fwd_fee +
93
                     (2 * gas_consumption + min_tons_for_storage)); ;; TODO(shahar) ?
94
                     ;; universal message send fee calculation may be activated here
95
                     ;; by using this instead of fwd_fee
96
                     ;; msg_fwd_fee(to_wallet, msg_body, state_init, 15)
97

98
  send_raw_message(msg.end_cell(), 64); ;; revert on errors
99
  save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
100
}
101

102
{-
103
  internal_transfer  query_id:uint64 amount:(VarUInteger 16) from:MsgAddress
104
                     response_address:MsgAddress
105
                     forward_ton_amount:(VarUInteger 16)
106
                     forward_payload:(Either Cell ^Cell) 
107
                     = InternalMsgBody;
108
-}
109

110
() receive_tokens (slice in_msg_body, slice sender_address, int my_ton_balance, int fwd_fee, int msg_value) impure {
111
  ;; NOTE we can not allow fails in action phase since in that case there will be
112
  ;; no bounce. Thus check and throw in computation phase.
113
  (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
114
  int query_id = in_msg_body~load_uint(64);
115
  int jetton_amount = in_msg_body~load_coins();
116
  balance += jetton_amount;
117
  slice from_address = in_msg_body~load_msg_addr();
118
  slice response_address = in_msg_body~load_msg_addr();
119
  throw_unless(707,
120
      equal_slices(jetton_master_address, sender_address)
121
      |
122
      equal_slices(calculate_user_jetton_wallet_address(from_address, jetton_master_address, jetton_wallet_code), sender_address)
123
  );
124
  int forward_ton_amount = in_msg_body~load_coins();
125

126
  int ton_balance_before_msg = my_ton_balance - msg_value;
127
  int storage_fee = min_tons_for_storage - min(ton_balance_before_msg, min_tons_for_storage);
128
  msg_value -= (storage_fee + gas_consumption);
129
  if(forward_ton_amount) {
130
    msg_value -= (forward_ton_amount + fwd_fee);
131
    slice either_forward_payload = in_msg_body;
132

133
    var msg_body = begin_cell()
134
        .store_uint(op::transfer_notification(), 32)
135
        .store_uint(query_id, 64)
136
        .store_coins(jetton_amount)
137
        .store_slice(from_address)
138
        .store_slice(either_forward_payload)
139
        .end_cell();
140

141
    var msg = begin_cell()
142
      .store_uint(0x10, 6) ;; we should not bounce here cause receiver can have uninitialized contract
143
      .store_slice(owner_address)
144
      .store_coins(forward_ton_amount)
145
      .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
146
      .store_ref(msg_body);
147

148
    send_raw_message(msg.end_cell(), 1);
149
  }
150

151
  if ((response_address.preload_uint(2) != 0) & (msg_value > 0)) {
152
    var msg = begin_cell()
153
      .store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 010000
154
      .store_slice(response_address)
155
      .store_coins(msg_value)
156
      .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
157
      .store_uint(op::excesses(), 32)
158
      .store_uint(query_id, 64);
159
    send_raw_message(msg.end_cell(), 2);
160
  }
161

162
  save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
163
}
164

165
() burn_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure {
166
  ;; NOTE we can not allow fails in action phase since in that case there will be
167
  ;; no bounce. Thus check and throw in computation phase.
168
  (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
169
  int query_id = in_msg_body~load_uint(64);
170
  int jetton_amount = in_msg_body~load_coins();
171
  slice response_address = in_msg_body~load_msg_addr();
172
  ;; ignore custom payload
173
  ;; slice custom_payload = in_msg_body~load_dict();
174
  balance -= jetton_amount;
175
  throw_unless(705, equal_slices(owner_address, sender_address));
176
  throw_unless(706, balance >= 0);
177
  throw_unless(707, msg_value > fwd_fee + 2 * gas_consumption);
178

179
  var msg_body = begin_cell()
180
      .store_uint(op::burn_notification(), 32)
181
      .store_uint(query_id, 64)
182
      .store_coins(jetton_amount)
183
      .store_slice(owner_address)
184
      .store_slice(response_address)
185
      .end_cell();
186

187
  var msg = begin_cell()
188
    .store_uint(0x18, 6)
189
    .store_slice(jetton_master_address)
190
    .store_coins(0)
191
    .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
192
    .store_ref(msg_body);
193

194
  send_raw_message(msg.end_cell(), 64);
195

196
  save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
197
}
198

199
() on_bounce (slice in_msg_body) impure {
200
  in_msg_body~skip_bits(32); ;; 0xFFFFFFFF
201
  (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
202
  int op = in_msg_body~load_uint(32);
203
  throw_unless(709, (op == op::internal_transfer()) | (op == op::burn_notification()));
204
  int query_id = in_msg_body~load_uint(64);
205
  int jetton_amount = in_msg_body~load_coins();
206
  balance += jetton_amount;
207
  save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
208
}
209

210
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
211
  if (in_msg_body.slice_empty?()) { ;; ignore empty messages
212
    return ();
213
  }
214

215
  slice cs = in_msg_full.begin_parse();
216
  int flags = cs~load_uint(4);
217
  if (flags & 1) {
218
    on_bounce(in_msg_body);
219
    return ();
220
  }
221
  slice sender_address = cs~load_msg_addr();
222
  cs~load_msg_addr(); ;; skip dst
223
  cs~load_coins(); ;; skip value
224
  cs~skip_bits(1); ;; skip extracurrency collection
225
  cs~load_coins(); ;; skip ihr_fee
226
  int fwd_fee = muldiv(cs~load_coins(), 3, 2); ;; we use message fwd_fee for estimation of forward_payload costs 
227

228
  int op = in_msg_body~load_uint(32);
229

230
  if (op == op::transfer()) { ;; outgoing transfer
231
    send_tokens(in_msg_body, sender_address, msg_value, fwd_fee);
232
    return ();
233
  }
234

235
  if (op == op::internal_transfer()) { ;; incoming transfer
236
    receive_tokens(in_msg_body, sender_address, my_balance, fwd_fee, msg_value);
237
    return ();
238
  }
239

240
  if (op == op::burn()) { ;; burn
241
    burn_tokens(in_msg_body, sender_address, msg_value, fwd_fee);
242
    return ();
243
  }
244

245
  throw(0xffff);
246
}
247

248
(int, slice, slice, cell) get_wallet_data() method_id {
249
  return load_data();
250
}
251

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.