Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compilables/ClaimHelper.compile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { CompilerConfig } from '@ton/blueprint';

export const compile: CompilerConfig = {
lang: 'tact',
target: 'contracts/rewards/claim-helper.tact',
};
6 changes: 6 additions & 0 deletions compilables/JettonMaster.compile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { CompilerConfig } from '@ton/blueprint';

export const compile: CompilerConfig = {
lang: 'func',
targets: ['contracts/jettons/standard/jetton_master.fc'],
};
6 changes: 6 additions & 0 deletions compilables/JettonVault.compile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { CompilerConfig } from '@ton/blueprint';

export const compile: CompilerConfig = {
lang: 'tact',
target: 'contracts/rewards/jetton-vault.tact',
};
6 changes: 6 additions & 0 deletions compilables/JettonWallet.compile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { CompilerConfig } from '@ton/blueprint';

export const compile: CompilerConfig = {
lang: 'func',
targets: ['contracts/jettons/standard/jetton_wallet.fc'],
};
6 changes: 6 additions & 0 deletions compilables/RewardJettonMaster.compile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { CompilerConfig } from '@ton/blueprint';

export const compile: CompilerConfig = {
lang: 'func',
targets: ['contracts/jettons/reward/reward_jetton_master.fc'],
};
8 changes: 8 additions & 0 deletions contracts/jetton/messages.tact
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ message(0x7362d09c) TokenNotification {
from: Address;
forward_payload: Slice as remaining;
}
// "op::transfer_notification"c (FunC)
message(0x4fb8dedc) TransferNotification {
queryId: Int as uint64;
amount: Int as coins;
from: Address;
forward_payload: Slice as remaining;
}

message(0x595f07bc) TokenBurn {
queryId: Int as uint64;
amount: Int as coins;
Expand Down
39 changes: 39 additions & 0 deletions contracts/jettons/common/jetton_utils.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "./params.fc";
#include "./op_codes.fc";

cell pack_jetton_wallet_data(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return begin_cell()
.store_coins(balance)
.store_slice(owner_address)
.store_slice(jetton_master_address)
.store_ref(jetton_wallet_code)
.end_cell();
}

;; get StateInit
cell calculate_jetton_wallet_state_init(slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return begin_cell()
.store_uint(0, 2)
.store_dict(jetton_wallet_code) ;; code
.store_dict(pack_jetton_wallet_data(0, owner_address, jetton_master_address, jetton_wallet_code)) ;; data
.store_uint(0, 1)
.end_cell();
}

slice calculate_jetton_wallet_address(cell state_init) inline {
return begin_cell().store_uint(4, 3)
.store_int(workchain(), 8)
.store_uint(cell_hash(state_init), 256)
.end_cell()
.begin_parse();
}

slice calculate_user_jetton_wallet_address(slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return calculate_jetton_wallet_address(
calculate_jetton_wallet_state_init(
owner_address,
jetton_master_address,
jetton_wallet_code
));
}

17 changes: 17 additions & 0 deletions contracts/jettons/common/op_codes.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const op::transfer = "op::transfer"c;
const op::transfer_notification = "op::transfer_notification"c;
const op::internal_transfer = "op::internal_transfer"c;
const op::excesses = "op::excesses"c;
const op::burn = "op::burn"c;
const op::burn_notification = "op::burn_notification"c;

;; Minter
const op::receiveInit = "op::receiveInit"c;
const op::mint = "op::mint"c;
const op::mint = "op::mint"c;

const op::redeemMessage = "op::redeemMessage"c;

;; Discovery params
const op::provide_wallet_address = "op::provide_wallet_address"c;
const op::take_wallet_address = "op::take_wallet_address"c;
12 changes: 12 additions & 0 deletions contracts/jettons/common/params.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
int workchain() asm "0 PUSHINT";

() force_chain(slice addr) impure {
(int wc, _) = parse_std_addr(addr);
throw_unless(333, wc == workchain());
}

int is_resolvable?(slice addr) inline {
(int wc, _) = parse_std_addr(addr);

return wc == workchain();
}
198 changes: 198 additions & 0 deletions contracts/jettons/reward/reward_jetton_master.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#include "../../imports/stdlib.fc";
#include "../common/jetton_utils.fc";
#include "../common/params.fc";
#include "../common/op_codes.fc";

const op::batch_mint = "op::batch_mint"c;

;; Jettons discoverable smart contract

(int, 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_ref(), ;; content
ds~load_ref() ;; jetton_wallet_code
);
}

() save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) impure inline {
begin_cell()
.store_coins(total_supply)
.store_slice(admin_address)
.store_ref(content)
.store_ref(jetton_wallet_code)
.end_cell()
.set_data();
}

() mint_tokens(slice to_address, cell jetton_wallet_code, int amount, cell master_msg) 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 msg = begin_cell()
.store_uint(0x18, 6) ;; bounceable
.store_slice(to_wallet_address)
.store_coins(amount)
.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
}

() send_text_message( slice to_addr, int value, int mode, builder content) impure {
var body = begin_cell()
.store_uint(0, 32)
.store_builder(content)
.end_cell();

var msg = begin_cell()
.store_uint(0x10, 6) ;; nobounce
.store_slice(to_addr)
.store_coins(value)
.store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_ref(body)
.end_cell();

send_raw_message(msg, mode);
}


;; =========================== Main Entry Points ===========================
() recv_internal(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 = muldiv(cs~load_coins(), 3, 2);
;; we use message fwd_fee for estimation of forward_payload costs

;; check in_msg_body message
int op = in_msg_body~load_uint(32);
int query_id = in_msg_body~load_uint(64);

(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data();

if (op == op::receiveInit) { ;; 0x0000015
slice to_address = in_msg_body~load_msg_addr();
builder msg_body = begin_cell().store_uint(0, 32).store_slice("Hello world!");
send_text_message(to_address, 0, 64, msg_body);
return ();
}

if (op == op::mint) {
throw_unless(73, equal_slices(sender_address, admin_address)); ;; only admin can mint - Wrapper Contract

slice to_address = in_msg_body~load_msg_addr();
int amount = in_msg_body~load_coins();
cell master_msg = in_msg_body~load_ref(); ;; load a reference message

slice master_msg_cs = master_msg.begin_parse();
master_msg_cs~skip_bits(32 + 64); ;; op + query_id
int jetton_amount = master_msg_cs~load_coins();

mint_tokens(to_address, jetton_wallet_code, amount, master_msg);
save_data(total_supply + jetton_amount, admin_address, content, jetton_wallet_code);
return ();
}
if (op == op::batch_mint) {
;; TODO: implement batch mint
;; throw_unless(73, equal_slices(sender_address, admin_address));
;; cell master_msg = in_msg_body~load_ref(); ;; load a reference message
;; slice master_msg_cs = master_msg.begin_parse();
;; master_msg_cs~skip_bits(32 + 64); ;; op + query_id
;; int dict_size = master_msg_cs~load_uint(16);
;; (slice s, cell dictionary_cell)= master_msg_cs~load_dict();
;; int added_jettons = 0;
;; (int key, slice val, int flag) = dictionary_cell.udict_get_min?(32);
;; while (flag) {
;; slice to_addr = begin_cell().store_uint(key, 267).end_cell().begin_parse();
;; int val_as_int = val~load_coins();
;; mint_tokens(to_addr, jetton_wallet_code, val_as_int, master_msg);
;; added_jettons += val_as_int;
;; (key, val, flag) = dictionary_cell.udict_get_next?(32, key);
;; }
;; save_data(total_supply + added_jettons, 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)
);
save_data(total_supply - jetton_amount, admin_address, content, jetton_wallet_code);

slice response_address = in_msg_body~load_msg_addr();
if (response_address.preload_uint(2) != 0) {
var msg = begin_cell()
.store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
.store_slice(response_address)
.store_coins(88)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
;; ------ Op Code & Message body --------
.store_uint(op::excesses, 32)
.store_uint(query_id, 64);
send_raw_message(msg.end_cell(), 1);

var returnWrapper_msg = begin_cell()
.store_uint(0x10, 6)
.store_slice(admin_address)
.store_coins(0)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
;; ------ Op Code & Message body --------
.store_uint(3852451109, 32) ;; 0xe59fbd25
.store_uint(query_id, 64)
.store_coins(jetton_amount)
.store_slice(response_address);
send_raw_message(returnWrapper_msg.end_cell(), 64);
}
return ();
}

if (op == 3) { ;; change admin
throw_unless(73, equal_slices(sender_address, admin_address));
slice new_admin_address = in_msg_body~load_msg_addr();
save_data(total_supply, new_admin_address, content, jetton_wallet_code);
return ();
}

if (op == 4) { ;; change content, delete this for immutable tokens
throw_unless(73, equal_slices(sender_address, admin_address));
save_data(total_supply, admin_address, in_msg_body~load_ref(), jetton_wallet_code);
return ();
}

throw(0xffff);
}


;; ------ Get Method ------
(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_wallet_address(slice owner_address) method_id {
(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data();
return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code);
}
Loading