|
|
|
;; Wrapped TON minter smart contract
|
|
|
|
|
|
|
|
;; storage scheme
|
|
|
|
;; storage#_ total_supply:Coins admin_address:MsgAddress next_admin_address:MsgAddress content:^Cell jetton_wallet_code:^Cell = Storage;
|
|
|
|
|
|
|
|
#pragma version >=0.2.0;
|
|
|
|
|
|
|
|
#include "imports/stdlib.fc";
|
|
|
|
#include "imports/op-codes.fc";
|
|
|
|
#include "imports/utils.fc";
|
|
|
|
#include "imports/discovery-params.fc";
|
|
|
|
|
|
|
|
int gas_consumption() asm "10000000 PUSHINT"; ;; 0.01 TON
|
|
|
|
int withdraw_gas_consumption() asm "10000000 PUSHINT"; ;; 0.01 TON
|
|
|
|
int deposit_gas_consumption() asm "35000000 PUSHINT"; ;; 0.035 TON
|
|
|
|
|
|
|
|
(int, slice, slice, cell, cell) load_data() inline {
|
|
|
|
slice ds = get_data().begin_parse();
|
|
|
|
return (
|
|
|
|
ds~load_coins(), ;; total_supply
|
|
|
|
ds~load_msg_addr(), ;; admin_address
|
|
|
|
ds~load_msg_addr(), ;; next_admin_address
|
|
|
|
ds~load_ref(), ;; content
|
|
|
|
ds~load_ref() ;; jetton_wallet_code
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
() save_data(int total_supply, slice admin_address, slice next_admin_address, cell content, cell jetton_wallet_code) impure inline {
|
|
|
|
set_data(begin_cell()
|
|
|
|
.store_coins(total_supply)
|
|
|
|
.store_slice(admin_address)
|
|
|
|
.store_slice(next_admin_address)
|
|
|
|
.store_ref(content)
|
|
|
|
.store_ref(jetton_wallet_code)
|
|
|
|
.end_cell()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
() mint_tokens(slice to_address, cell jetton_wallet_code, int amount) impure {
|
|
|
|
cell state_init = calculate_jetton_wallet_state_init(to_address, my_address(), jetton_wallet_code);
|
|
|
|
slice to_wallet_address = calculate_jetton_wallet_address(state_init);
|
|
|
|
var master_msg = begin_cell()
|
|
|
|
.store_uint(op::internal_transfer, 32)
|
|
|
|
;; query_id
|
|
|
|
.store_uint(0, 64)
|
|
|
|
;; jetton_amount
|
|
|
|
.store_coins(amount)
|
|
|
|
;; from_address == to_address
|
|
|
|
.store_slice(to_address)
|
|
|
|
;; response_address
|
|
|
|
.store_slice(my_address())
|
|
|
|
;; forward ton amount
|
|
|
|
.store_coins(0)
|
|
|
|
.end_cell();
|
|
|
|
var msg = begin_cell()
|
|
|
|
.store_uint(0x18, 6)
|
|
|
|
.store_slice(to_wallet_address)
|
|
|
|
.store_coins(gas_consumption())
|
|
|
|
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
|
|
|
|
.store_ref(state_init)
|
|
|
|
.store_ref(master_msg);
|
|
|
|
send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
|
|
|
|
}
|
|
|
|
|
|
|
|
() burn_tokens(slice to_address, cell jetton_wallet_code, int amount) impure {
|
|
|
|
cell state_init = calculate_jetton_wallet_state_init(to_address, my_address(), jetton_wallet_code);
|
|
|
|
slice to_wallet_address = calculate_jetton_wallet_address(state_init);
|
|
|
|
var master_msg = begin_cell()
|
|
|
|
.store_uint(op::burn, 32)
|
|
|
|
;; query_id
|
|
|
|
.store_uint(0, 64)
|
|
|
|
;; jetton_amount
|
|
|
|
.store_coins(amount)
|
|
|
|
;; response_address
|
|
|
|
.store_slice(my_address())
|
|
|
|
;; custom_payload (currently not used)
|
|
|
|
.store_dict(new_dict())
|
|
|
|
.end_cell();
|
|
|
|
var msg = begin_cell()
|
|
|
|
.store_uint(0x18, 6)
|
|
|
|
.store_slice(to_wallet_address)
|
|
|
|
.store_coins(gas_consumption() * 3)
|
|
|
|
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
|
|
|
|
.store_ref(state_init)
|
|
|
|
.store_ref(master_msg);
|
|
|
|
send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
|
|
|
|
}
|
|
|
|
|
|
|
|
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
|
|
|
|
if (in_msg_body.slice_empty?()) { ;; ignore empty messages
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
slice cs = in_msg_full.begin_parse();
|
|
|
|
int flags = cs~load_uint(4);
|
|
|
|
|
|
|
|
if (flags & 1) { ;; ignore all bounced messages
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
slice sender_address = cs~load_msg_addr();
|
|
|
|
cs~load_msg_addr(); ;; skip dst
|
|
|
|
cs~load_coins(); ;; skip value
|
|
|
|
cs~skip_bits(1); ;; skip extracurrency collection
|
|
|
|
cs~load_coins(); ;; skip ihr_fee
|
|
|
|
int fwd_fee = cs~load_coins(); ;; we use message fwd_fee for estimation of provide_wallet_address cost
|
|
|
|
|
|
|
|
int op = in_msg_body~load_uint(32);
|
|
|
|
int query_id = in_msg_body~load_uint(64);
|
|
|
|
|
|
|
|
(int total_supply, slice admin_address, slice next_admin_address, cell content, cell jetton_wallet_code) = load_data();
|
|
|
|
|
|
|
|
if (op == op::deposit) {
|
|
|
|
msg_value -= deposit_gas_consumption();
|
|
|
|
throw_if(74, msg_value < 0);
|
|
|
|
|
|
|
|
int ton_amount = msg_value;
|
|
|
|
int jetton_amount = in_msg_body~load_coins();
|
|
|
|
throw_if(75, ton_amount < jetton_amount);
|
|
|
|
mint_tokens(sender_address, jetton_wallet_code, jetton_amount);
|
|
|
|
save_data(total_supply + jetton_amount, admin_address, next_admin_address, content, jetton_wallet_code);
|
|
|
|
|
|
|
|
msg_value -= jetton_amount + gas_consumption();
|
|
|
|
if (msg_value > 0) {
|
|
|
|
var msg = begin_cell()
|
|
|
|
.store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 010000
|
|
|
|
.store_slice(sender_address)
|
|
|
|
.store_coins(msg_value)
|
|
|
|
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
|
|
.store_uint(op::excesses, 32)
|
|
|
|
.store_uint(query_id, 64);
|
|
|
|
send_raw_message(msg.end_cell(), 2); ;; pay transfer fees together, ignore errors
|
|
|
|
}
|
|
|
|
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op == op::withdraw) {
|
|
|
|
msg_value -= withdraw_gas_consumption();
|
|
|
|
throw_if(74, msg_value < 0);
|
|
|
|
|
|
|
|
int jetton_amount = in_msg_body~load_coins();
|
|
|
|
throw_if(75, total_supply < jetton_amount);
|
|
|
|
burn_tokens(sender_address, jetton_wallet_code, jetton_amount);
|
|
|
|
save_data(total_supply, admin_address, next_admin_address, content, jetton_wallet_code);
|
|
|
|
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op == op::burn_notification) {
|
|
|
|
int jetton_amount = in_msg_body~load_coins();
|
|
|
|
slice from_address = in_msg_body~load_msg_addr();
|
|
|
|
throw_unless(74,
|
|
|
|
equal_slices(calculate_user_jetton_wallet_address(from_address, my_address(), jetton_wallet_code), sender_address)
|
|
|
|
);
|
|
|
|
|
|
|
|
if (jetton_amount > 0) {
|
|
|
|
var msg = begin_cell()
|
|
|
|
.store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 010000
|
|
|
|
.store_slice(from_address)
|
|
|
|
.store_coins(jetton_amount)
|
|
|
|
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
|
|
;; op code = 0
|
|
|
|
.store_uint(0, 32)
|
|
|
|
.store_uint(query_id, 64);
|
|
|
|
send_raw_message(msg.end_cell(), 1); ;; pay transfer fees seperatly, revert on errors
|
|
|
|
}
|
|
|
|
|
|
|
|
save_data(total_supply - jetton_amount, admin_address, next_admin_address, content, jetton_wallet_code);
|
|
|
|
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op == op::provide_wallet_address) {
|
|
|
|
throw_unless(75, msg_value > fwd_fee + gas_consumption());
|
|
|
|
|
|
|
|
slice owner_address = in_msg_body~load_msg_addr();
|
|
|
|
int include_address? = in_msg_body~load_uint(1);
|
|
|
|
|
|
|
|
cell included_address = include_address?
|
|
|
|
? begin_cell().store_slice(owner_address).end_cell()
|
|
|
|
: null();
|
|
|
|
|
|
|
|
var msg = begin_cell()
|
|
|
|
.store_uint(0x18, 6)
|
|
|
|
.store_slice(sender_address)
|
|
|
|
.store_coins(0)
|
|
|
|
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
|
|
.store_uint(op::take_wallet_address, 32)
|
|
|
|
.store_uint(query_id, 64);
|
|
|
|
|
|
|
|
if (is_resolvable?(owner_address)) {
|
|
|
|
msg = msg.store_slice(calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code));
|
|
|
|
} else {
|
|
|
|
msg = msg.store_uint(0, 2); ;; addr_none
|
|
|
|
}
|
|
|
|
send_raw_message(msg.store_maybe_ref(included_address).end_cell(), 64);
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op == op::change_next_admin) { ;; change next admin
|
|
|
|
throw_unless(73, equal_slices(sender_address, admin_address));
|
|
|
|
slice new_next_admin_address = in_msg_body~load_msg_addr();
|
|
|
|
save_data(total_supply, admin_address, new_next_admin_address, content, jetton_wallet_code);
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op == op::change_admin) { ;; change admin (only next admin can get an admin permission)
|
|
|
|
throw_unless(73, equal_slices(sender_address, next_admin_address));
|
|
|
|
slice addr_none = begin_cell().store_uint(0, 2).end_cell().begin_parse();
|
|
|
|
save_data(total_supply, next_admin_address, addr_none, content, jetton_wallet_code);
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op == op::change_content) { ;; change content, delete this for immutable tokens
|
|
|
|
throw_unless(73, equal_slices(sender_address, admin_address));
|
|
|
|
save_data(total_supply, admin_address, next_admin_address, in_msg_body~load_ref(), jetton_wallet_code);
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op == op::excesses) {
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw(0xffff);
|
|
|
|
}
|
|
|
|
|
|
|
|
(int, int, slice, cell, cell) get_jetton_data() method_id {
|
|
|
|
(int total_supply, slice admin_address, _, cell content, cell jetton_wallet_code) = load_data();
|
|
|
|
return (total_supply, -1, admin_address, content, jetton_wallet_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
(slice) get_jetton_extra_data() method_id {
|
|
|
|
(_, _, slice next_admin_address, _, _) = load_data();
|
|
|
|
return (next_admin_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
slice get_wallet_address(slice owner_address) method_id {
|
|
|
|
(_, _, _, _, cell jetton_wallet_code) = load_data();
|
|
|
|
return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code);
|
|
|
|
}
|