mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Merge branch 'testnet' into accelerator
This commit is contained in:
commit
62ede1851b
216 changed files with 23301 additions and 246 deletions
|
@ -1589,6 +1589,9 @@ forget @proclist forget @proccnt
|
|||
{ }END> b> } : }END>c
|
||||
{ }END>c <s } : }END>s
|
||||
|
||||
// This is the way how FunC assigns method_id for reserved functions.
|
||||
// Note, that Tolk entrypoints have other names (`onInternalMessage`, etc.),
|
||||
// but method_id is assigned not by Fift, but by Tolk code generation.
|
||||
0 constant recv_internal
|
||||
-1 constant recv_external
|
||||
-2 constant run_ticktock
|
||||
|
|
|
@ -114,7 +114,7 @@ class MemoryFileLoader : public fift::FileLoader {
|
|||
std::map<std::string, std::string, std::less<>> files_;
|
||||
};
|
||||
|
||||
td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_preamble = true, bool need_asm = true,
|
||||
td::Result<fift::SourceLookup> create_source_lookup(std::string&& main, bool need_preamble = true, bool need_asm = true,
|
||||
bool need_ton_util = true, bool need_lisp = true,
|
||||
bool need_w3_code = true, bool need_fift_ext = true,
|
||||
bool need_disasm = true, std::string dir = "") {
|
||||
|
@ -189,7 +189,7 @@ td::Result<fift::SourceLookup> run_fift(fift::SourceLookup source_lookup, std::o
|
|||
} // namespace
|
||||
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
|
||||
std::stringstream ss;
|
||||
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, true, true, fift_dir));
|
||||
TRY_RESULT(source_lookup, create_source_lookup(std::move(source), true, true, true, true, true, true, true, fift_dir));
|
||||
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
|
||||
FiftOutput res;
|
||||
res.source_lookup = std::move(source_lookup);
|
||||
|
@ -207,19 +207,43 @@ td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std:
|
|||
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir, bool need_preamble,
|
||||
bool need_asm, bool need_ton_util, bool need_lisp,
|
||||
bool need_w3_code) {
|
||||
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, false, false,
|
||||
return create_source_lookup(std::move(main), need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, false, false,
|
||||
fift_dir);
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) {
|
||||
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code) {
|
||||
std::stringstream ss;
|
||||
TRY_RESULT(source_lookup,
|
||||
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n"
|
||||
<< (is_raw ? "}>c" : "") << " boc>B \"res\" B>file",
|
||||
true, true, true, false, false, false, false, fift_dir));
|
||||
std::string sb;
|
||||
sb.reserve(asm_code.size() + 100);
|
||||
sb.append("\"Asm.fif\" include\n <{\n");
|
||||
sb.append(asm_code.data(), asm_code.size());
|
||||
sb.append("\n}>c boc>B \"res\" B>file");
|
||||
|
||||
TRY_RESULT(source_lookup, create_source_lookup(std::move(sb), true, true, true, false, false, false, false));
|
||||
TRY_RESULT(res, run_fift(std::move(source_lookup), &ss));
|
||||
TRY_RESULT(boc, res.read_file("res"));
|
||||
return vm::std_boc_deserialize(std::move(boc.data));
|
||||
}
|
||||
|
||||
td::Result<CompiledProgramOutput> compile_asm_program(std::string&& program_code, const std::string& fift_dir) {
|
||||
std::string main_fif;
|
||||
main_fif.reserve(program_code.size() + 100);
|
||||
main_fif.append(program_code.data(), program_code.size());
|
||||
main_fif.append(R"( dup hashB B>X $>B "hex" B>file)"); // write codeHashHex to a file
|
||||
main_fif.append(R"( boc>B B>base64 $>B "boc" B>file)"); // write codeBoc64 to a file
|
||||
|
||||
std::stringstream fift_output_stream;
|
||||
TRY_RESULT(source_lookup, create_source_lookup(std::move(main_fif), true, true, false, false, false, false, false, fift_dir));
|
||||
TRY_RESULT(res, run_fift(std::move(source_lookup), &fift_output_stream));
|
||||
|
||||
TRY_RESULT(boc, res.read_file("boc"));
|
||||
TRY_RESULT(hex, res.read_file("hex"));
|
||||
|
||||
return CompiledProgramOutput{
|
||||
std::move(program_code),
|
||||
std::move(boc.data),
|
||||
std::move(hex.data),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace fift
|
||||
|
|
|
@ -26,11 +26,21 @@ struct FiftOutput {
|
|||
SourceLookup source_lookup;
|
||||
std::string output;
|
||||
};
|
||||
|
||||
// given a valid Fift code PROGRAM{ ... }END>c, compile_asm_program() returns this output
|
||||
// now it's used primarily for wasm output (see tolk-js, for example)
|
||||
struct CompiledProgramOutput {
|
||||
std::string fiftCode;
|
||||
std::string codeBoc64;
|
||||
std::string codeHashHex;
|
||||
};
|
||||
|
||||
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir = "",
|
||||
bool need_preamble = true, bool need_asm = true,
|
||||
bool need_ton_util = true, bool need_lisp = true,
|
||||
bool need_w3_code = true);
|
||||
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args = {}, std::string fift_dir = "");
|
||||
td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std::string> args);
|
||||
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir = "", bool is_raw = true);
|
||||
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code);
|
||||
td::Result<CompiledProgramOutput> compile_asm_program(std::string&& program_code, const std::string& fift_dir);
|
||||
} // namespace fift
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
int foo(int x) method_id(11) {
|
||||
int foo(int x) {
|
||||
try {
|
||||
if (x == 7) {
|
||||
throw(44);
|
||||
|
@ -9,7 +9,7 @@ int foo(int x) method_id(11) {
|
|||
}
|
||||
}
|
||||
|
||||
int foo_inline(int x) inline method_id(12) {
|
||||
int foo_inline(int x) inline {
|
||||
try {
|
||||
if (x == 7) {
|
||||
throw(44);
|
||||
|
@ -20,7 +20,7 @@ int foo_inline(int x) inline method_id(12) {
|
|||
}
|
||||
}
|
||||
|
||||
int foo_inlineref(int x) inline_ref method_id(13) {
|
||||
int foo_inlineref(int x) inline_ref {
|
||||
try {
|
||||
if (x == 7) {
|
||||
throw(44);
|
||||
|
@ -31,17 +31,17 @@ int foo_inlineref(int x) inline_ref method_id(13) {
|
|||
}
|
||||
}
|
||||
|
||||
int test(int x, int y, int z) method_id(1) {
|
||||
int test(int x, int y, int z) method_id(101) {
|
||||
y = foo(y);
|
||||
return x * 100 + y * 10 + z;
|
||||
}
|
||||
|
||||
int test_inline(int x, int y, int z) method_id(2) {
|
||||
int test_inline(int x, int y, int z) method_id(102) {
|
||||
y = foo_inline(y);
|
||||
return x * 100 + y * 10 + z;
|
||||
}
|
||||
|
||||
int test_inlineref(int x, int y, int z) method_id(3) {
|
||||
int test_inlineref(int x, int y, int z) method_id(103) {
|
||||
y = foo_inlineref(y);
|
||||
return x * 100 + y * 10 + z;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ int test_inlineref(int x, int y, int z) method_id(3) {
|
|||
int foo_inline_big(
|
||||
int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10,
|
||||
int x11, int x12, int x13, int x14, int x15, int x16, int x17, int x18, int x19, int x20
|
||||
) inline method_id(14) {
|
||||
) inline {
|
||||
try {
|
||||
if (x1 == 7) {
|
||||
throw(44);
|
||||
|
@ -60,7 +60,7 @@ int foo_inline_big(
|
|||
}
|
||||
}
|
||||
|
||||
int test_inline_big(int x, int y, int z) method_id(4) {
|
||||
int test_inline_big(int x, int y, int z) method_id(104) {
|
||||
y = foo_inline_big(
|
||||
y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9,
|
||||
y + 10, y + 11, y + 12, y + 13, y + 14, y + 15, y + 16, y + 17, y + 18, y + 19);
|
||||
|
@ -70,7 +70,7 @@ int test_inline_big(int x, int y, int z) method_id(4) {
|
|||
int foo_big(
|
||||
int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10,
|
||||
int x11, int x12, int x13, int x14, int x15, int x16, int x17, int x18, int x19, int x20
|
||||
) method_id(15) {
|
||||
) {
|
||||
try {
|
||||
if (x1 == 7) {
|
||||
throw(44);
|
||||
|
@ -81,29 +81,69 @@ int foo_big(
|
|||
}
|
||||
}
|
||||
|
||||
int test_big(int x, int y, int z) method_id(5) {
|
||||
int test_big(int x, int y, int z) method_id(105) {
|
||||
y = foo_big(
|
||||
y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9,
|
||||
y + 10, y + 11, y + 12, y + 13, y + 14, y + 15, y + 16, y + 17, y + 18, y + 19);
|
||||
return x * 1000000 + y * 1000 + z;
|
||||
}
|
||||
|
||||
|
||||
() some_throwing(int op) impure {
|
||||
if (op == 1) {
|
||||
return ();
|
||||
} elseif (op == 2) {
|
||||
return ();
|
||||
} else {
|
||||
throw(1);
|
||||
}
|
||||
}
|
||||
|
||||
int test106() method_id(106) {
|
||||
try {
|
||||
some_throwing(1337);
|
||||
return 1337;
|
||||
} catch(_, code) {
|
||||
return code;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
global int g_reg;
|
||||
|
||||
(int, int) test107() method_id(107) {
|
||||
int l_reg = 10;
|
||||
g_reg = 10;
|
||||
try {
|
||||
;; note, that regardless of assignment, an exception RESTORES them to previous (to 10)
|
||||
;; it's very unexpected, but is considered to be a TVM feature, not a bug
|
||||
g_reg = 999;
|
||||
l_reg = 999;
|
||||
some_throwing(999);
|
||||
} catch(_, _) {
|
||||
}
|
||||
;; returns (10,10) because of an exception, see a comment above
|
||||
return (g_reg, l_reg);
|
||||
}
|
||||
|
||||
() main() {
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 1 | 1 2 3 | 123
|
||||
TESTCASE | 1 | 3 8 9 | 389
|
||||
TESTCASE | 1 | 3 7 9 | 329
|
||||
TESTCASE | 2 | 1 2 3 | 123
|
||||
TESTCASE | 2 | 3 8 9 | 389
|
||||
TESTCASE | 2 | 3 7 9 | 329
|
||||
TESTCASE | 3 | 1 2 3 | 123
|
||||
TESTCASE | 3 | 3 8 9 | 389
|
||||
TESTCASE | 3 | 3 7 9 | 329
|
||||
TESTCASE | 4 | 4 8 9 | 4350009
|
||||
TESTCASE | 4 | 4 7 9 | 4001009
|
||||
TESTCASE | 5 | 4 8 9 | 4350009
|
||||
TESTCASE | 5 | 4 7 9 | 4001009
|
||||
method_id | in | out
|
||||
TESTCASE | 101 | 1 2 3 | 123
|
||||
TESTCASE | 101 | 3 8 9 | 389
|
||||
TESTCASE | 101 | 3 7 9 | 329
|
||||
TESTCASE | 102 | 1 2 3 | 123
|
||||
TESTCASE | 102 | 3 8 9 | 389
|
||||
TESTCASE | 102 | 3 7 9 | 329
|
||||
TESTCASE | 103 | 1 2 3 | 123
|
||||
TESTCASE | 103 | 3 8 9 | 389
|
||||
TESTCASE | 103 | 3 7 9 | 329
|
||||
TESTCASE | 104 | 4 8 9 | 4350009
|
||||
TESTCASE | 104 | 4 7 9 | 4001009
|
||||
TESTCASE | 105 | 4 8 9 | 4350009
|
||||
TESTCASE | 105 | 4 7 9 | 4001009
|
||||
TESTCASE | 106 | | 1
|
||||
TESTCASE | 107 | | 10 10
|
||||
-}
|
||||
|
|
132
crypto/func/auto-tests/tests/var-apply.fc
Normal file
132
crypto/func/auto-tests/tests/var-apply.fc
Normal file
|
@ -0,0 +1,132 @@
|
|||
tuple empty_tuple() asm "NIL";
|
||||
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
|
||||
builder begin_cell() asm "NEWC";
|
||||
cell end_cell(builder b) asm "ENDC";
|
||||
slice begin_parse(cell c) asm "CTOS";
|
||||
|
||||
|
||||
_ getBeginCell() {
|
||||
return begin_cell;
|
||||
}
|
||||
|
||||
_ getBeginParse() {
|
||||
return begin_parse;
|
||||
}
|
||||
|
||||
(int, int) test101() method_id(101) {
|
||||
var (_, f_end_cell) = (0, end_cell);
|
||||
builder b = (getBeginCell())().store_int(1, 32);
|
||||
b~store_int(2, 32);
|
||||
var s = (getBeginParse())(f_end_cell(b));
|
||||
return (s~load_int(32), s~load_int(32));
|
||||
}
|
||||
|
||||
() my_throw_always() inline {
|
||||
throw(1000);
|
||||
}
|
||||
|
||||
_ get_raiser() inline {
|
||||
return my_throw_always;
|
||||
}
|
||||
|
||||
int test102() method_id(102) {
|
||||
try {
|
||||
var raiser = get_raiser();
|
||||
raiser(); ;; `some_var()` is always impure, the compiler has no considerations about its runtime value
|
||||
return 0;
|
||||
} catch (_, code) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
int sum(int a, int b) impure inline {
|
||||
throw_unless(1000, a + b < 24);
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int mul(int a, int b) impure inline {
|
||||
throw_unless(1001, a * b < 24);
|
||||
return a * b;
|
||||
}
|
||||
|
||||
int sum_pure(int a, int b) inline {
|
||||
throw_unless(1000, a + b < 24);
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int mul_pure(int a, int b) inline {
|
||||
throw_unless(1001, a * b < 24);
|
||||
return a * b;
|
||||
}
|
||||
|
||||
int demo_handler(int op, int query_id, int a, int b) {
|
||||
if (op == 0xF2) {
|
||||
var func = query_id % 2 == 0 ? sum : mul;
|
||||
int result = func(a, b);
|
||||
return 0; ;; result not used, we test that func is nevertheless called
|
||||
}
|
||||
if (op == 0xF3) {
|
||||
var func = query_id % 2 == 0 ? sum_pure : mul_pure;
|
||||
int result = func(a, b);
|
||||
return 0; ;; the same for sum_pure, since `some_var()` is always impure
|
||||
}
|
||||
if (op == 0xF4) {
|
||||
var func = query_id % 2 == 0 ? sum : mul;
|
||||
int result = func(a, b);
|
||||
return result;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
tuple test103() method_id(103) {
|
||||
tuple t = empty_tuple();
|
||||
try {
|
||||
t~tpush(demo_handler(0xF2, 122, 100, 200));
|
||||
} catch(_, code) {
|
||||
t~tpush(code);
|
||||
}
|
||||
try {
|
||||
t~tpush(demo_handler(0xF4, 122, 100, 200));
|
||||
} catch(_, code) {
|
||||
t~tpush(code);
|
||||
}
|
||||
try {
|
||||
t~tpush(demo_handler(0xF3, 122, 10, 10));
|
||||
} catch(_, code) {
|
||||
t~tpush(code);
|
||||
}
|
||||
try {
|
||||
t~tpush(demo_handler(0xF3, 123, 10, 10));
|
||||
} catch(_, code) {
|
||||
t~tpush(code);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
() always_throw2(int x) impure {
|
||||
throw (239 + x);
|
||||
}
|
||||
|
||||
global int -> () global_f;
|
||||
|
||||
int test104() method_id(104) {
|
||||
try {
|
||||
global_f = always_throw2;
|
||||
global_f(1);
|
||||
return 0;
|
||||
} catch (_, code) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
() main() {
|
||||
}
|
||||
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 101 | | 1 2
|
||||
TESTCASE | 102 | | 1000
|
||||
TESTCASE | 103 | | [ 1000 1000 0 1001 ]
|
||||
TESTCASE | 104 | | 240
|
||||
-}
|
|
@ -830,6 +830,8 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
catch_stack.push_new_var(left[1]);
|
||||
stack.rearrange_top(catch_vars, catch_last);
|
||||
stack.opt_show();
|
||||
stack.o << "c1 PUSH";
|
||||
stack.o << "c3 PUSH";
|
||||
stack.o << "c4 PUSH";
|
||||
stack.o << "c5 PUSH";
|
||||
stack.o << "c7 PUSH";
|
||||
|
@ -846,6 +848,8 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
stack.o << "c7 SETCONT";
|
||||
stack.o << "c5 SETCONT";
|
||||
stack.o << "c4 SETCONT";
|
||||
stack.o << "c3 SETCONT";
|
||||
stack.o << "c1 SETCONT";
|
||||
for (size_t begin = catch_vars.size(), end = begin; end > 0; end = begin) {
|
||||
begin = end >= block_size ? end - block_size : 0;
|
||||
stack.o << std::to_string(end - begin) + " PUSHINT";
|
||||
|
|
|
@ -45,7 +45,7 @@ extern std::string generated_from;
|
|||
|
||||
constexpr int optimize_depth = 20;
|
||||
|
||||
const std::string func_version{"0.4.5"};
|
||||
const std::string func_version{"0.4.6"};
|
||||
|
||||
enum Keyword {
|
||||
_Eof = -1,
|
||||
|
|
|
@ -380,7 +380,10 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
|
|||
}
|
||||
res.push_back(tfunc[0]);
|
||||
auto rvect = new_tmp_vect(code);
|
||||
code.emplace_back(here, Op::_CallInd, rvect, std::move(res));
|
||||
auto& op = code.emplace_back(here, Op::_CallInd, rvect, std::move(res));
|
||||
if (flags & _IsImpure) {
|
||||
op.flags |= Op::_Impure;
|
||||
}
|
||||
return rvect;
|
||||
}
|
||||
case _Const: {
|
||||
|
|
|
@ -427,7 +427,7 @@ Expr* make_func_apply(Expr* fun, Expr* x) {
|
|||
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
|
||||
} else {
|
||||
res = new Expr{Expr::_VarApply, {fun, x}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
res->flags = Expr::_IsRvalue | Expr::_IsImpure; // for `some_var()`, don't make any considerations about runtime value, it's impure
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -37,10 +37,10 @@
|
|||
|
||||
td::Result<std::string> compile_internal(char *config_json) {
|
||||
TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json)))
|
||||
auto &obj = input_json.get_object();
|
||||
td::JsonObject& config = input_json.get_object();
|
||||
|
||||
TRY_RESULT(opt_level, td::get_json_object_int_field(obj, "optLevel", false));
|
||||
TRY_RESULT(sources_obj, td::get_json_object_field(obj, "sources", td::JsonValue::Type::Array, false));
|
||||
TRY_RESULT(opt_level, td::get_json_object_int_field(config, "optLevel", false));
|
||||
TRY_RESULT(sources_obj, td::get_json_object_field(config, "sources", td::JsonValue::Type::Array, false));
|
||||
|
||||
auto &sources_arr = sources_obj.get_array();
|
||||
|
||||
|
@ -52,29 +52,25 @@ td::Result<std::string> compile_internal(char *config_json) {
|
|||
|
||||
funC::opt_level = std::max(0, opt_level);
|
||||
funC::program_envelope = true;
|
||||
funC::asm_preamble = true;
|
||||
funC::verbosity = 0;
|
||||
funC::indent = 1;
|
||||
|
||||
std::ostringstream outs, errs;
|
||||
auto compile_res = funC::func_proceed(sources, outs, errs);
|
||||
|
||||
if (compile_res != 0) {
|
||||
return td::Status::Error(std::string("Func compilation error: ") + errs.str());
|
||||
int funC_res = funC::func_proceed(sources, outs, errs);
|
||||
if (funC_res != 0) {
|
||||
return td::Status::Error("FunC compilation error: " + errs.str());
|
||||
}
|
||||
|
||||
TRY_RESULT(code_cell, fift::compile_asm(outs.str(), "/fiftlib/", false));
|
||||
TRY_RESULT(boc, vm::std_boc_serialize(code_cell));
|
||||
TRY_RESULT(fift_res, fift::compile_asm_program(outs.str(), "/fiftlib/"));
|
||||
|
||||
td::JsonBuilder result_json;
|
||||
auto result_obj = result_json.enter_object();
|
||||
result_obj("status", "ok");
|
||||
result_obj("codeBoc", td::base64_encode(boc));
|
||||
result_obj("fiftCode", outs.str());
|
||||
result_obj("codeHashHex", code_cell->get_hash().to_hex());
|
||||
result_obj.leave();
|
||||
|
||||
outs.clear();
|
||||
errs.clear();
|
||||
auto obj = result_json.enter_object();
|
||||
obj("status", "ok");
|
||||
obj("fiftCode", std::move(fift_res.fiftCode));
|
||||
obj("codeBoc", std::move(fift_res.codeBoc64));
|
||||
obj("codeHashHex", std::move(fift_res.codeHashHex));
|
||||
obj.leave();
|
||||
|
||||
return result_json.string_builder().as_cslice().str();
|
||||
}
|
||||
|
|
747
crypto/smartcont/tolk-stdlib/common.tolk
Normal file
747
crypto/smartcont/tolk-stdlib/common.tolk
Normal file
|
@ -0,0 +1,747 @@
|
|||
// Standard library for Tolk (LGPL licence).
|
||||
// It contains common functions that are available out of the box, the user doesn't have to import anything.
|
||||
// More specific functions are required to be imported explicitly, like "@stdlib/tvm-dicts".
|
||||
tolk 0.6
|
||||
|
||||
/**
|
||||
Tuple manipulation primitives.
|
||||
Elements of a tuple can be of arbitrary type.
|
||||
Note that atomic type `tuple` can't be cast to composite tuple type (e.g. `[int, cell]`) and vise versa.
|
||||
*/
|
||||
|
||||
/// Creates a tuple with zero elements.
|
||||
@pure
|
||||
fun createEmptyTuple(): tuple
|
||||
asm "NIL";
|
||||
|
||||
/// Appends a value to tuple, resulting in `Tuple t' = (x1, ..., xn, value)`.
|
||||
/// If its size exceeds 255, throws a type check exception.
|
||||
@pure
|
||||
fun tuplePush<X>(mutate self: tuple, value: X): void
|
||||
asm "TPUSH";
|
||||
|
||||
/// Returns the first element of a non-empty tuple.
|
||||
@pure
|
||||
fun tupleFirst<X>(t: tuple): X
|
||||
asm "FIRST";
|
||||
|
||||
/// Returns the [`index`]-th element of a tuple.
|
||||
@pure
|
||||
fun tupleAt<X>(t: tuple, index: int): X
|
||||
builtin;
|
||||
|
||||
/// Returns the size of a tuple (elements count in it).
|
||||
@pure
|
||||
fun tupleSize(t: tuple): int
|
||||
asm "TLEN";
|
||||
|
||||
/// Returns the last element of a non-empty tuple.
|
||||
@pure
|
||||
fun tupleLast(t: tuple): int
|
||||
asm "LAST";
|
||||
|
||||
|
||||
/**
|
||||
Mathematical primitives.
|
||||
*/
|
||||
|
||||
/// Computes the minimum of two integers.
|
||||
@pure
|
||||
fun min(x: int, y: int): int
|
||||
asm "MIN";
|
||||
|
||||
/// Computes the maximum of two integers.
|
||||
@pure
|
||||
fun max(x: int, y: int): int
|
||||
asm "MAX";
|
||||
|
||||
/// Sorts two integers.
|
||||
@pure
|
||||
fun minMax(x: int, y: int): (int, int)
|
||||
asm "MINMAX";
|
||||
|
||||
/// Computes the absolute value of an integer.
|
||||
@pure
|
||||
fun abs(x: int): int
|
||||
asm "ABS";
|
||||
|
||||
/// Returns the sign of an integer: `-1` if x < 0, `0` if x == 0, `1` if x > 0.
|
||||
@pure
|
||||
fun sign(x: int): int
|
||||
asm "SGN";
|
||||
|
||||
/// Computes the quotient and remainder of [x] / [y]. Example: divMod(112,3) = (37,1)
|
||||
@pure
|
||||
fun divMod(x: int, y: int): (int, int)
|
||||
asm "DIVMOD";
|
||||
|
||||
/// Computes the remainder and quotient of [x] / [y]. Example: modDiv(112,3) = (1,37)
|
||||
@pure
|
||||
fun modDiv(x: int, y: int): (int, int)
|
||||
asm(-> 1 0) "DIVMOD";
|
||||
|
||||
/// Computes multiple-then-divide: floor([x] * [y] / [z]).
|
||||
/// The intermediate result is stored in a 513-bit integer to prevent precision loss.
|
||||
@pure
|
||||
fun mulDivFloor(x: int, y: int, z: int): int
|
||||
builtin;
|
||||
|
||||
/// Similar to `mulDivFloor`, but rounds the result: round([x] * [y] / [z]).
|
||||
@pure
|
||||
fun mulDivRound(x: int, y: int, z: int): int
|
||||
builtin;
|
||||
|
||||
/// Similar to `mulDivFloor`, but ceils the result: ceil([x] * [y] / [z]).
|
||||
@pure
|
||||
fun mulDivCeil(x: int, y: int, z: int): int
|
||||
builtin;
|
||||
|
||||
/// Computes the quotient and remainder of ([x] * [y] / [z]). Example: mulDivMod(112,3,10) = (33,6)
|
||||
@pure
|
||||
fun mulDivMod(x: int, y: int, z: int): (int, int)
|
||||
builtin;
|
||||
|
||||
|
||||
/**
|
||||
Global getters of environment and contract state.
|
||||
*/
|
||||
|
||||
const MASTERCHAIN = -1;
|
||||
const BASECHAIN = 0;
|
||||
|
||||
/// Returns current Unix timestamp (in seconds).
|
||||
@pure
|
||||
fun now(): int
|
||||
asm "NOW";
|
||||
|
||||
/// Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`.
|
||||
/// If necessary, it can be parsed further using primitives such as [parseStandardAddress].
|
||||
@pure
|
||||
fun getMyAddress(): slice
|
||||
asm "MYADDR";
|
||||
|
||||
/// Returns the balance (in nanotoncoins) of the smart contract at the start of Computation Phase.
|
||||
/// Note that RAW primitives such as [sendMessage] do not update this field.
|
||||
@pure
|
||||
fun getMyOriginalBalance(): int
|
||||
asm "BALANCE" "FIRST";
|
||||
|
||||
/// Same as [getMyOriginalBalance], but returns a tuple:
|
||||
/// `int` — balance in nanotoncoins;
|
||||
/// `cell` — a dictionary with 32-bit keys representing the balance of "extra currencies".
|
||||
@pure
|
||||
fun getMyOriginalBalanceWithExtraCurrencies(): [int, cell]
|
||||
asm "BALANCE";
|
||||
|
||||
/// Returns the logical time of the current transaction.
|
||||
@pure
|
||||
fun getLogicalTime(): int
|
||||
asm "LTIME";
|
||||
|
||||
/// Returns the starting logical time of the current block.
|
||||
@pure
|
||||
fun getCurrentBlockLogicalTime(): int
|
||||
asm "BLOCKLT";
|
||||
|
||||
/// Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value.
|
||||
@pure
|
||||
fun getBlockchainConfigParam(x: int): cell
|
||||
asm "CONFIGOPTPARAM";
|
||||
|
||||
/// Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later.
|
||||
@pure
|
||||
fun getContractData(): cell
|
||||
asm "c4 PUSH";
|
||||
|
||||
/// Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive.
|
||||
fun setContractData(c: cell): void
|
||||
asm "c4 POP";
|
||||
|
||||
/// Retrieves code of smart-contract from c7
|
||||
@pure
|
||||
fun getContractCode(): cell
|
||||
asm "MYCODE";
|
||||
|
||||
/// Creates an output action that would change this smart contract code to that given by cell [newCode].
|
||||
/// Notice that this change will take effect only after the successful termination of the current run of the smart contract.
|
||||
fun setContractCodePostponed(newCode: cell): void
|
||||
asm "SETCODE";
|
||||
|
||||
/// Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”)
|
||||
/// so that the current execution is considered “successful” with the saved values even if an exception
|
||||
/// in Computation Phase is thrown later.
|
||||
fun commitContractDataAndActions(): void
|
||||
asm "COMMIT";
|
||||
|
||||
|
||||
/**
|
||||
Signature checks, hashing, cryptography.
|
||||
*/
|
||||
|
||||
/// Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`.
|
||||
/// Useful for signing and checking signatures of arbitrary entities represented by a tree of cells.
|
||||
@pure
|
||||
fun cellHash(c: cell): int
|
||||
asm "HASHCU";
|
||||
|
||||
/// Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`.
|
||||
/// The result is the same as if an ordinary cell containing only data and references from `s` had been created
|
||||
/// and its hash computed by [cellHash].
|
||||
@pure
|
||||
fun sliceHash(s: slice): int
|
||||
asm "HASHSU";
|
||||
|
||||
/// Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight,
|
||||
/// throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`.
|
||||
@pure
|
||||
fun stringHash(s: slice): int
|
||||
asm "SHA256U";
|
||||
|
||||
/// Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data)
|
||||
/// using [publicKey] (also represented by a 256-bit unsigned integer).
|
||||
/// The signature must contain at least 512 data bits; only the first 512 bits are used.
|
||||
/// The result is `−1` if the signature is valid, `0` otherwise.
|
||||
/// Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`.
|
||||
/// That is, if [hash] is computed as the hash of some data, these data are hashed twice,
|
||||
/// the second hashing occurring inside `CHKSIGNS`.
|
||||
@pure
|
||||
fun isSignatureValid(hash: int, signature: slice, publicKey: int): int
|
||||
asm "CHKSIGNU";
|
||||
|
||||
/// Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `publicKey`,
|
||||
/// similarly to [isSignatureValid].
|
||||
/// If the bit length of [data] is not divisible by eight, throws a cell underflow exception.
|
||||
/// The verification of Ed25519 signatures is the standard one,
|
||||
/// with sha256 used to reduce [data] to the 256-bit number that is actually signed.
|
||||
@pure
|
||||
fun isSliceSignatureValid(data: slice, signature: slice, publicKey: int): int
|
||||
asm "CHKSIGNS";
|
||||
|
||||
/// Generates a new pseudo-random unsigned 256-bit integer x.
|
||||
fun random(): int
|
||||
asm "RANDU256";
|
||||
|
||||
/// Generates a new pseudo-random integer z in the range 0..range−1 (or range..−1, if range < 0).
|
||||
/// More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed.
|
||||
fun randomRange(range: int): int
|
||||
asm "RAND";
|
||||
|
||||
/// Returns the current random seed as an unsigned 256-bit integer.
|
||||
@pure
|
||||
fun randomGetSeed(): int
|
||||
asm "RANDSEED";
|
||||
|
||||
/// Sets the random seed to unsigned 256-bit seed.
|
||||
fun randomSetSeed(seed: int): void
|
||||
asm "SETRAND";
|
||||
|
||||
/// Initializes (mixes) random seed with unsigned 256-bit integer x.
|
||||
fun randomizeBy(x: int): void
|
||||
asm "ADDRAND";
|
||||
|
||||
/// Initializes random seed using current time. Don't forget to call this before calling `random`!
|
||||
fun randomizeByLogicalTime(): void
|
||||
asm "LTIME" "ADDRAND";
|
||||
|
||||
|
||||
/**
|
||||
Size computation primitives.
|
||||
They may be useful for computing storage fees of user-provided data.
|
||||
*/
|
||||
|
||||
/// Returns `(x, y, z, -1)` or `(null, null, null, 0)`.
|
||||
/// Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z`
|
||||
/// in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account
|
||||
/// the identification of equal cells.
|
||||
/// The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG,
|
||||
/// with a hash table of visited cell hashes used to prevent visits of already-visited cells.
|
||||
/// The total count of visited cells `x` cannot exceed non-negative [maxCells];
|
||||
/// otherwise the computation is aborted before visiting the `(maxCells + 1)`-st cell and
|
||||
/// a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`.
|
||||
@pure
|
||||
fun calculateCellSize(c: cell, maxCells: int): (int, int, int, int)
|
||||
asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
|
||||
|
||||
/// Similar to [calculateCellSize], but accepting a `slice` [s] instead of a `cell`.
|
||||
/// The returned value of `x` does not take into account the cell that contains the `slice` [s] itself;
|
||||
/// however, the data bits and the cell references of [s] are accounted for in `y` and `z`.
|
||||
@pure
|
||||
fun calculateSliceSize(s: slice, maxCells: int): (int, int, int, int)
|
||||
asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
|
||||
|
||||
/// A non-quiet version of [calculateCellSize] that throws a cell overflow exception (`8`) on failure.
|
||||
fun calculateCellSizeStrict(c: cell, maxCells: int): (int, int, int)
|
||||
asm "CDATASIZE";
|
||||
|
||||
/// A non-quiet version of [calculateSliceSize] that throws a cell overflow exception (`8`) on failure.
|
||||
fun calculateSliceSizeStrict(s: slice, maxCells: int): (int, int, int)
|
||||
asm "SDATASIZE";
|
||||
|
||||
/// Returns the depth of `cell` [c].
|
||||
/// If [c] has no references, then return `0`;
|
||||
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [c].
|
||||
/// If [c] is a `null` instead of a cell, returns zero.
|
||||
@pure
|
||||
fun getCellDepth(c: cell): int
|
||||
asm "CDEPTH";
|
||||
|
||||
/// Returns the depth of `slice` [s].
|
||||
/// If [s] has no references, then returns `0`;
|
||||
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [s].
|
||||
@pure
|
||||
fun getSliceDepth(s: slice): int
|
||||
asm "SDEPTH";
|
||||
|
||||
/// Returns the depth of `builder` [b].
|
||||
/// If no cell references are stored in [b], then returns 0;
|
||||
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [b].
|
||||
@pure
|
||||
fun getBuilderDepth(b: builder): int
|
||||
asm "BDEPTH";
|
||||
|
||||
|
||||
/**
|
||||
Debug primitives.
|
||||
Only works for local TVM execution with debug level verbosity.
|
||||
*/
|
||||
|
||||
/// Dump a variable [x] to the debug log.
|
||||
fun debugPrint<X>(x: X): void
|
||||
builtin;
|
||||
|
||||
/// Dump a string [x] to the debug log.
|
||||
fun debugPrintString<X>(x: X): void
|
||||
builtin;
|
||||
|
||||
/// Dumps the stack (at most the top 255 values) and shows the total stack depth.
|
||||
fun debugDumpStack(): void
|
||||
builtin;
|
||||
|
||||
|
||||
/**
|
||||
Slice primitives: parsing cells.
|
||||
When you _load_ some data, you mutate the slice (shifting an internal pointer on the stack).
|
||||
When you _preload_ some data, you just get the result without mutating the slice.
|
||||
*/
|
||||
|
||||
/// Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell,
|
||||
/// or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2)
|
||||
/// which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards.
|
||||
@pure
|
||||
fun beginParse(c: cell): slice
|
||||
asm "CTOS";
|
||||
|
||||
/// Checks if slice is empty. If not, throws an exception.
|
||||
fun assertEndOfSlice(self: slice): void
|
||||
asm "ENDS";
|
||||
|
||||
/// Loads the next reference from the slice.
|
||||
@pure
|
||||
fun loadRef(mutate self: slice): cell
|
||||
asm( -> 1 0) "LDREF";
|
||||
|
||||
/// Preloads the next reference from the slice.
|
||||
@pure
|
||||
fun preloadRef(self: slice): cell
|
||||
asm "PLDREF";
|
||||
|
||||
/// Loads a signed [len]-bit integer from a slice.
|
||||
@pure
|
||||
fun loadInt(mutate self: slice, len: int): int
|
||||
builtin;
|
||||
|
||||
/// Loads an unsigned [len]-bit integer from a slice.
|
||||
@pure
|
||||
fun loadUint(mutate self: slice, len: int): int
|
||||
builtin;
|
||||
|
||||
/// Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice `s''`.
|
||||
@pure
|
||||
fun loadBits(mutate self: slice, len: int): slice
|
||||
builtin;
|
||||
|
||||
/// Preloads a signed [len]-bit integer from a slice.
|
||||
@pure
|
||||
fun preloadInt(self: slice, len: int): int
|
||||
builtin;
|
||||
|
||||
/// Preloads an unsigned [len]-bit integer from a slice.
|
||||
@pure
|
||||
fun preloadUint(self: slice, len: int): int
|
||||
builtin;
|
||||
|
||||
/// Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice.
|
||||
@pure
|
||||
fun preloadBits(self: slice, len: int): slice
|
||||
builtin;
|
||||
|
||||
/// Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`).
|
||||
@pure
|
||||
fun loadCoins(mutate self: slice): int
|
||||
asm( -> 1 0) "LDGRAMS";
|
||||
|
||||
/// Loads bool (-1 or 0) from a slice
|
||||
@pure
|
||||
fun loadBool(mutate self: slice): int
|
||||
asm( -> 1 0) "1 LDI";
|
||||
|
||||
/// Shifts a slice pointer to [len] bits forward, mutating the slice.
|
||||
@pure
|
||||
fun skipBits(mutate self: slice, len: int): self
|
||||
asm "SDSKIPFIRST";
|
||||
|
||||
/// Returns the first `0 ≤ len ≤ 1023` bits of a slice.
|
||||
@pure
|
||||
fun getFirstBits(self: slice, len: int): slice
|
||||
asm "SDCUTFIRST";
|
||||
|
||||
/// Returns all but the last `0 ≤ len ≤ 1023` bits of a slice.
|
||||
@pure
|
||||
fun removeLastBits(mutate self: slice, len: int): self
|
||||
asm "SDSKIPLAST";
|
||||
|
||||
/// Returns the last `0 ≤ len ≤ 1023` bits of a slice.
|
||||
@pure
|
||||
fun getLastBits(self: slice, len: int): slice
|
||||
asm "SDCUTLAST";
|
||||
|
||||
/// Loads a dictionary (TL HashMapE structure, represented as TVM cell) from a slice.
|
||||
/// Returns `null` if `nothing` constructor is used.
|
||||
@pure
|
||||
fun loadDict(mutate self: slice): cell
|
||||
asm( -> 1 0) "LDDICT";
|
||||
|
||||
/// Preloads a dictionary (cell) from a slice.
|
||||
@pure
|
||||
fun preloadDict(self: slice): cell
|
||||
asm "PLDDICT";
|
||||
|
||||
/// Loads a dictionary as [loadDict], but returns only the remainder of the slice.
|
||||
@pure
|
||||
fun skipDict(mutate self: slice): self
|
||||
asm "SKIPDICT";
|
||||
|
||||
/// Loads (Maybe ^Cell) from a slice.
|
||||
/// In other words, loads 1 bit: if it's true, loads the first ref, otherwise returns `null`.
|
||||
@pure
|
||||
fun loadMaybeRef(mutate self: slice): cell
|
||||
asm( -> 1 0) "LDOPTREF";
|
||||
|
||||
/// Preloads (Maybe ^Cell) from a slice.
|
||||
@pure
|
||||
fun preloadMaybeRef(self: slice): cell
|
||||
asm "PLDOPTREF";
|
||||
|
||||
/// Loads (Maybe ^Cell), but returns only the remainder of the slice.
|
||||
@pure
|
||||
fun skipMaybeRef(mutate self: slice): self
|
||||
asm "SKIPOPTREF";
|
||||
|
||||
/**
|
||||
Builder primitives: constructing cells.
|
||||
When you _store_ some data, you mutate the builder (shifting an internal pointer on the stack).
|
||||
All the primitives below first check whether there is enough space in the `builder`,
|
||||
and only then check the range of the value being serialized.
|
||||
*/
|
||||
|
||||
/// Creates a new empty builder.
|
||||
@pure
|
||||
fun beginCell(): builder
|
||||
asm "NEWC";
|
||||
|
||||
/// Converts a builder into an ordinary `cell`.
|
||||
@pure
|
||||
fun endCell(self: builder): cell
|
||||
asm "ENDC";
|
||||
|
||||
/// Stores a reference to a cell into a builder.
|
||||
@pure
|
||||
fun storeRef(mutate self: builder, c: cell): self
|
||||
asm(c self) "STREF";
|
||||
|
||||
/// Stores a signed [len]-bit integer into a builder (`0 ≤ len ≤ 257`).
|
||||
@pure
|
||||
fun storeInt(mutate self: builder, x: int, len: int): self
|
||||
builtin;
|
||||
|
||||
/// Stores an unsigned [len]-bit integer into a builder (`0 ≤ len ≤ 256`).
|
||||
@pure
|
||||
fun storeUint(mutate self: builder, x: int, len: int): self
|
||||
builtin;
|
||||
|
||||
/// Stores a slice into a builder.
|
||||
@pure
|
||||
fun storeSlice(mutate self: builder, s: slice): self
|
||||
asm "STSLICER";
|
||||
|
||||
/// Stores amount of Toncoins into a builder.
|
||||
@pure
|
||||
fun storeCoins(mutate self: builder, x: int): self
|
||||
asm "STGRAMS";
|
||||
|
||||
/// Stores bool (-1 or 0) into a builder.
|
||||
/// Attention: true value is `-1`, not 1! If you pass `1` here, TVM will throw an exception.
|
||||
@pure
|
||||
fun storeBool(mutate self: builder, x: int): self
|
||||
asm(x self) "1 STI";
|
||||
|
||||
/// Stores dictionary (represented by TVM `cell` or `null`) into a builder.
|
||||
/// In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise.
|
||||
@pure
|
||||
fun storeDict(mutate self: builder, c: cell): self
|
||||
asm(c self) "STDICT";
|
||||
|
||||
/// Stores (Maybe ^Cell) into a builder.
|
||||
/// In other words, if cell is `null`, store '0' bit; otherwise, store '1' and a ref to [c].
|
||||
@pure
|
||||
fun storeMaybeRef(mutate self: builder, c: cell): self
|
||||
asm(c self) "STOPTREF";
|
||||
|
||||
/// Concatenates two builders.
|
||||
@pure
|
||||
fun storeBuilder(mutate self: builder, from: builder): self
|
||||
asm "STBR";
|
||||
|
||||
/// Stores a slice representing TL addr_none$00 (two `0` bits).
|
||||
@pure
|
||||
fun storeAddressNone(mutate self: builder): self
|
||||
asm "b{00} STSLICECONST";
|
||||
|
||||
|
||||
/**
|
||||
Slice size primitives.
|
||||
*/
|
||||
|
||||
/// Returns the number of references in a slice.
|
||||
@pure
|
||||
fun getRemainingRefsCount(self: slice): int
|
||||
asm "SREFS";
|
||||
|
||||
/// Returns the number of data bits in a slice.
|
||||
@pure
|
||||
fun getRemainingBitsCount(self: slice): int
|
||||
asm "SBITS";
|
||||
|
||||
/// Returns both the number of data bits and the number of references in a slice.
|
||||
@pure
|
||||
fun getRemainingBitsAndRefsCount(self: slice): (int, int)
|
||||
asm "SBITREFS";
|
||||
|
||||
/// Checks whether a slice is empty (i.e., contains no bits of data and no cell references).
|
||||
@pure
|
||||
fun isEndOfSlice(self: slice): int
|
||||
asm "SEMPTY";
|
||||
|
||||
/// Checks whether a slice has no bits of data.
|
||||
@pure
|
||||
fun isEndOfSliceBits(self: slice): int
|
||||
asm "SDEMPTY";
|
||||
|
||||
/// Checks whether a slice has no references.
|
||||
@pure
|
||||
fun isEndOfSliceRefs(self: slice): int
|
||||
asm "SREMPTY";
|
||||
|
||||
/// Checks whether data parts of two slices coinside.
|
||||
@pure
|
||||
fun isSliceBitsEqual(self: slice, b: slice): int
|
||||
asm "SDEQ";
|
||||
|
||||
/// Returns the number of cell references already stored in a builder.
|
||||
@pure
|
||||
fun getBuilderRefsCount(self: builder): int
|
||||
asm "BREFS";
|
||||
|
||||
/// Returns the number of data bits already stored in a builder.
|
||||
@pure
|
||||
fun getBuilderBitsCount(self: builder): int
|
||||
asm "BBITS";
|
||||
|
||||
|
||||
/**
|
||||
Address manipulation primitives.
|
||||
The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme:
|
||||
```TL-B
|
||||
addr_none$00 = MsgAddressExt;
|
||||
addr_extern$01 len:(## 8) external_address:(bits len)
|
||||
= MsgAddressExt;
|
||||
anycast_info$_ depth:(#<= 30) { depth >= 1 }
|
||||
rewrite_pfx:(bits depth) = Anycast;
|
||||
addr_std$10 anycast:(Maybe Anycast)
|
||||
workchain_id:int8 address:bits256 = MsgAddressInt;
|
||||
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
|
||||
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
|
||||
_ _:MsgAddressInt = MsgAddress;
|
||||
_ _:MsgAddressExt = MsgAddress;
|
||||
|
||||
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
|
||||
src:MsgAddress dest:MsgAddressInt
|
||||
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
|
||||
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
|
||||
ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
|
||||
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
|
||||
```
|
||||
A deserialized `MsgAddress` is represented by a tuple `t` as follows:
|
||||
|
||||
- `addr_none` is represented by `t = (0)`,
|
||||
i.e., a tuple containing exactly one integer equal to zero.
|
||||
- `addr_extern` is represented by `t = (1, s)`,
|
||||
where slice `s` contains the field `external_address`. In other words, `
|
||||
t` is a pair (a tuple consisting of two entries), containing an integer equal to one and slice `s`.
|
||||
- `addr_std` is represented by `t = (2, u, x, s)`,
|
||||
where `u` is either a `null` (if `anycast` is absent) or a slice `s'` containing `rewrite_pfx` (if anycast is present).
|
||||
Next, integer `x` is the `workchain_id`, and slice `s` contains the address.
|
||||
- `addr_var` is represented by `t = (3, u, x, s)`,
|
||||
where `u`, `x`, and `s` have the same meaning as for `addr_std`.
|
||||
*/
|
||||
|
||||
/// Loads from slice [s] the only prefix that is a valid `MsgAddress`,
|
||||
/// and returns both this prefix `s'` and the remainder `s''` of [s] as slices.
|
||||
@pure
|
||||
fun loadAddress(mutate self: slice): slice
|
||||
asm( -> 1 0) "LDMSGADDR";
|
||||
|
||||
/// Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`.
|
||||
/// If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown.
|
||||
@pure
|
||||
fun parseAddress(s: slice): tuple
|
||||
asm "PARSEMSGADDR";
|
||||
|
||||
/// Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`),
|
||||
/// applies rewriting from the anycast (if present) to the same-length prefix of the address,
|
||||
/// and returns both the workchain and the 256-bit address as integers.
|
||||
/// If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`,
|
||||
/// throws a cell deserialization exception.
|
||||
@pure
|
||||
fun parseStandardAddress(s: slice): (int, int)
|
||||
asm "REWRITESTDADDR";
|
||||
|
||||
/// Creates a slice representing TL addr_none$00 (two `0` bits).
|
||||
@pure
|
||||
fun createAddressNone(): slice
|
||||
asm "b{00} PUSHSLICE";
|
||||
|
||||
/// Returns if a slice pointer contains an empty address (`-1` for true, `0` for false, as always).
|
||||
/// In other words, a slice starts with two `0` bits (TL addr_none$00).
|
||||
@pure
|
||||
fun addressIsNone(s: slice): int
|
||||
asm "2 PLDU" "0 EQINT";
|
||||
|
||||
|
||||
/**
|
||||
Reserving Toncoins on balance and its flags.
|
||||
*/
|
||||
|
||||
/// mode = 0: Reserve exact amount of nanotoncoins
|
||||
const RESERVE_MODE_EXACT_AMOUNT = 0;
|
||||
/// +1: Actually reserves all but amount, meaning `currentContractBalance - amount`
|
||||
const RESERVE_MODE_ALL_BUT_AMOUNT = 1;
|
||||
/// +2: Actually set `min(amount, currentContractBalance)` (without this mode, if amount is greater, the action will fail)
|
||||
const RESERVE_MODE_AT_MOST = 2;
|
||||
/// +4: [amount] is increased by the _original_ balance of the current account (before the compute phase).
|
||||
const RESERVE_MODE_INCREASE_BY_ORIGINAL_BALANCE = 4;
|
||||
/// +8: Actually sets `amount = -amount` before performing any further actions.
|
||||
const RESERVE_MODE_NEGATE_AMOUNT = 8;
|
||||
/// +16: If this action fails, the transaction will be bounced.
|
||||
const RESERVE_MODE_BOUNCE_ON_ACTION_FAIL = 16;
|
||||
|
||||
/// Creates an output action which would reserve Toncoins on balance.
|
||||
/// For [reserveMode] consider constants above.
|
||||
fun reserveToncoinsOnBalance(nanoTonCoins: int, reserveMode: int): void
|
||||
asm "RAWRESERVE";
|
||||
|
||||
/// Similar to [reserveToncoinsOnBalance], but also accepts a dictionary extraAmount (represented by a cell or null)
|
||||
/// with extra currencies. In this way currencies other than Toncoin can be reserved.
|
||||
fun reserveExtraCurrenciesOnBalance(nanoTonCoins: int, extraAmount: cell, reserveMode: int): void
|
||||
asm "RAWRESERVEX";
|
||||
|
||||
|
||||
/**
|
||||
Messages sending and parsing primitives.
|
||||
Working with messages is low-level right now, but still, every contract should do that.
|
||||
|
||||
`Message` structure, its header and so on are specified in TL-B scheme, particularly:
|
||||
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool ... = CommonMsgInfo;
|
||||
*/
|
||||
|
||||
/// 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00
|
||||
const BOUNCEABLE = 0x18;
|
||||
/// 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00
|
||||
const NON_BOUNCEABLE = 0x10;
|
||||
|
||||
/// Load msgFlags from incoming message body (4 bits).
|
||||
@pure
|
||||
fun loadMessageFlags(mutate self: slice): int
|
||||
asm( -> 1 0) "4 LDU";
|
||||
|
||||
/// Having msgFlags (4 bits), check that a message is bounced.
|
||||
/// Effectively, it's `msgFlags & 1` (the lowest bit present).
|
||||
@pure
|
||||
fun isMessageBounced(msgFlags: int): int
|
||||
asm "1 PUSHINT" "AND";
|
||||
|
||||
/// Skip 0xFFFFFFFF prefix (when a message is bounced).
|
||||
@pure
|
||||
fun skipBouncedPrefix(mutate self: slice): self
|
||||
asm "32 PUSHINT" "SDSKIPFIRST";
|
||||
|
||||
/// The guideline recommends to start the body of an internal message with uint32 `op` and uint64 `queryId`.
|
||||
@pure
|
||||
fun loadMessageOp(mutate self: slice): int
|
||||
asm( -> 1 0) "32 LDU";
|
||||
|
||||
@pure
|
||||
fun skipMessageOp(mutate self: slice): self
|
||||
asm "32 PUSHINT" "SDSKIPFIRST";
|
||||
|
||||
@pure
|
||||
fun storeMessageOp(mutate self: builder, op: int): self
|
||||
asm(op self) "32 STU";
|
||||
|
||||
/// The guideline recommends that uint64 `queryId` should follow uint32 `op`.
|
||||
@pure
|
||||
fun loadMessageQueryId(mutate self: slice): int
|
||||
asm( -> 1 0) "64 LDU";
|
||||
|
||||
@pure
|
||||
fun skipMessageQueryId(mutate self: slice): self
|
||||
asm "64 PUSHINT" "SDSKIPFIRST";
|
||||
|
||||
@pure
|
||||
fun storeMessageQueryId(mutate self: builder, queryId: int): self
|
||||
asm(queryId self) "64 STU";
|
||||
|
||||
/// SEND MODES - https://docs.ton.org/tvm.pdf page 137, SENDRAWMSG
|
||||
|
||||
/// mode = 0 is used for ordinary messages; the gas fees are deducted from the senging amount; action phaes should NOT be ignored.
|
||||
const SEND_MODE_REGULAR = 0;
|
||||
/// +1 means that the sender wants to pay transfer fees separately.
|
||||
const SEND_MODE_PAY_FEES_SEPARATELY = 1;
|
||||
/// +2 means that any errors arising while processing this message during the action phase should be ignored.
|
||||
const SEND_MODE_IGNORE_ERRORS = 2;
|
||||
/// in the case of action fail - bounce transaction. No effect if SEND_MODE_IGNORE_ERRORS (+2) is used. TVM UPGRADE 2023-07. https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages
|
||||
const SEND_MODE_BOUNCE_ON_ACTION_FAIL = 16;
|
||||
/// mode = 32 means that the current account must be destroyed if its resulting balance is zero.
|
||||
const SEND_MODE_DESTROY = 32;
|
||||
/// mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message.
|
||||
const SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE = 64;
|
||||
/// mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message).
|
||||
const SEND_MODE_CARRY_ALL_BALANCE = 128;
|
||||
/// do not create an action, only estimate fee. TVM UPGRADE 2023-07. https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages
|
||||
const SEND_MODE_ESTIMATE_FEE_ONLY = 1024;
|
||||
/// Other modes affect the fee calculation as follows:
|
||||
/// +64 substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account).
|
||||
/// +128 substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account).
|
||||
|
||||
/// Sends a raw message — a correctly serialized TL object `Message X`.
|
||||
/// For `mode`, see constants above (except SEND_MODE_ESTIMATE_FEE_ONLY).
|
||||
/// This function is still available, but deprecated: consider using [sendMessage].
|
||||
@deprecated
|
||||
fun sendRawMessage(msg: cell, mode: int): void
|
||||
asm "SENDRAWMSG";
|
||||
|
||||
/// Creates an output action and returns a fee for creating a message.
|
||||
/// Mode has the same effect as in the case of SENDRAWMSG.
|
||||
/// For mode including SEND_MODE_ESTIMATE_FEE_ONLY it just returns estimated fee without sending a message.
|
||||
fun sendMessage(msg: cell, mode: int): int
|
||||
asm "SENDMSG";
|
63
crypto/smartcont/tolk-stdlib/gas-payments.tolk
Normal file
63
crypto/smartcont/tolk-stdlib/gas-payments.tolk
Normal file
|
@ -0,0 +1,63 @@
|
|||
// A part of standard library for Tolk
|
||||
tolk 0.6
|
||||
|
||||
/**
|
||||
Gas and payment related primitives.
|
||||
*/
|
||||
|
||||
/// Returns amount of gas (in gas units) consumed in current Computation Phase.
|
||||
fun getGasConsumedAtTheMoment(): int
|
||||
asm "GASCONSUMED";
|
||||
|
||||
/// This function is required to be called when you process an external message (from an outer world)
|
||||
/// and "accept" it to blockchain.
|
||||
/// Without calling this function, an external message would be discarded.
|
||||
/// As an effect, the current smart contract agrees to buy some gas to finish the current transaction.
|
||||
/// For more details, check [accept_message effects](https://ton.org/docs/#/smart-contracts/accept).
|
||||
fun acceptExternalMessage(): void
|
||||
asm "ACCEPT";
|
||||
|
||||
/// When processing an internal message, by default, the limit of gas consumption is determined by incoming message.
|
||||
/// Functions [setGasLimit] and [setGasLimitToMaximum] allow you to change this behavior.
|
||||
/// Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero,
|
||||
/// decreasing the value of `gr` by `gc` in the process.
|
||||
fun setGasLimitToMaximum(): void
|
||||
asm "ACCEPT";
|
||||
|
||||
/// When processing an internal message, by default, the limit of gas consumption is determined by incoming message.
|
||||
/// Functions [setGasLimit] and [setGasLimitToMaximum] allow you to change this behavior.
|
||||
/// Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero.
|
||||
/// If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`,
|
||||
/// an (unhandled) out of gas exception is thrown before setting new gas limits.
|
||||
fun setGasLimit(limit: int): void
|
||||
asm "SETGASLIMIT";
|
||||
|
||||
/// Calculates fee (amount in nanotoncoins to be paid) for a transaction which consumed [gasUsed] gas units.
|
||||
fun calculateGasFee(workchain: int, gasUsed: int): int
|
||||
asm(gasUsed workchain) "GETGASFEE";
|
||||
|
||||
/// Same as [calculateGasFee], but without flat price (you have supposed to read https://docs.ton.org/develop/howto/fees-low-level)
|
||||
fun calculateGasFeeWithoutFlatPrice(workchain: int, gasUsed: int): int
|
||||
asm(gasUsed workchain) "GETGASFEESIMPLE";
|
||||
|
||||
/// Calculates amount of nanotoncoins you should pay for storing a contract of provided size for [seconds].
|
||||
/// [bits] and [cells] represent contract state (code + data).
|
||||
fun calculateStorageFee(workchain: int, seconds: int, bits: int, cells: int): int
|
||||
asm(cells bits seconds workchain) "GETSTORAGEFEE";
|
||||
|
||||
/// Calculates amount of nanotoncoins you should pay to send a message of specified size.
|
||||
fun calculateMessageFee(workchain: int, bits: int, cells: int): int
|
||||
asm(cells bits workchain) "GETFORWARDFEE";
|
||||
|
||||
/// Same as [calculateMessageFee], but without lump price (you have supposed to read https://docs.ton.org/develop/howto/fees-low-level)
|
||||
fun calculateMessageFeeWithoutLumpPrice(workchain: int, bits: int, cells: int): int
|
||||
asm(cells bits workchain) "GETFORWARDFEESIMPLE";
|
||||
|
||||
/// Calculates fee that was paid by the sender of an incoming internal message.
|
||||
fun calculateOriginalMessageFee(workchain: int, incomingFwdFee: int): int
|
||||
asm(incomingFwdFee workchain) "GETORIGINALFWDFEE";
|
||||
|
||||
/// Returns the amount of nanotoncoins current contract debts for storage. ("due" and "debt" are synonyms)
|
||||
/// If it has no debt, `0` is returned.
|
||||
fun getMyStorageDuePayment(): int
|
||||
asm "DUEPAYMENT";
|
38
crypto/smartcont/tolk-stdlib/lisp-lists.tolk
Normal file
38
crypto/smartcont/tolk-stdlib/lisp-lists.tolk
Normal file
|
@ -0,0 +1,38 @@
|
|||
// A part of standard library for Tolk
|
||||
tolk 0.6
|
||||
|
||||
/**
|
||||
Lisp-style lists are nested 2-elements tuples: `(1, (2, (3, null)))` represents list `[1, 2, 3]`.
|
||||
Elements of a list can be of different types.
|
||||
Empty list is conventionally represented as TVM `null` value.
|
||||
*/
|
||||
|
||||
@pure
|
||||
fun createEmptyList(): tuple
|
||||
asm "PUSHNULL";
|
||||
|
||||
/// Adds an element to the beginning of lisp-style list.
|
||||
/// Note, that it does not mutate the list: instead, it returns a new one (it's a lisp pattern).
|
||||
@pure
|
||||
fun listPrepend<X>(head: X, tail: tuple): tuple
|
||||
asm "CONS";
|
||||
|
||||
/// Extracts the head and the tail of lisp-style list.
|
||||
@pure
|
||||
fun listSplit<X>(list: tuple): (X, tuple)
|
||||
asm "UNCONS";
|
||||
|
||||
/// Extracts the tail and the head of lisp-style list.
|
||||
@pure
|
||||
fun listNext<X>(mutate self: tuple): X
|
||||
asm( -> 1 0) "UNCONS";
|
||||
|
||||
/// Returns the head of lisp-style list.
|
||||
@pure
|
||||
fun listGetHead<X>(list: tuple): X
|
||||
asm "CAR";
|
||||
|
||||
/// Returns the tail of lisp-style list.
|
||||
@pure
|
||||
fun listGetTail(list: tuple): tuple
|
||||
asm "CDR";
|
311
crypto/smartcont/tolk-stdlib/tvm-dicts.tolk
Normal file
311
crypto/smartcont/tolk-stdlib/tvm-dicts.tolk
Normal file
|
@ -0,0 +1,311 @@
|
|||
// A part of standard library for Tolk
|
||||
tolk 0.6
|
||||
|
||||
/**
|
||||
Dictionaries are represented as `cell` data type (cells can store anything, dicts in particular).
|
||||
Currently, they have very low-level API very close to TVM internals.
|
||||
Most of functions are duplicated for three common cases:
|
||||
- iDict* - dicts with signed integer keys
|
||||
- uDict* - dicts with unsigned integer keys
|
||||
- sDict* - dicts with arbitrary slice keys
|
||||
When accessing a dict element, you should not only provide a key, but provide keyLen,
|
||||
since for optimization, for optimization, key length is not stored in the dictionary itself.
|
||||
*/
|
||||
|
||||
/// Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL
|
||||
@pure
|
||||
fun createEmptyDict(): cell
|
||||
asm "NEWDICT";
|
||||
|
||||
/// Checks whether a dictionary is empty.
|
||||
@pure
|
||||
fun dictIsEmpty(self: cell): int
|
||||
asm "DICTEMPTY";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGet(self: cell, keyLen: int, key: int): (slice, int)
|
||||
asm(key self keyLen) "DICTIGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun uDictGet(self: cell, keyLen: int, key: int): (slice, int)
|
||||
asm(key self keyLen) "DICTUGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun sDictGet(self: cell, keyLen: int, key: slice): (slice, int)
|
||||
asm(key self keyLen) "DICTGET" "NULLSWAPIFNOT";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
|
||||
asm(value key self keyLen) "DICTISET";
|
||||
|
||||
@pure
|
||||
fun uDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
|
||||
asm(value key self keyLen) "DICTUSET";
|
||||
|
||||
@pure
|
||||
fun sDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): void
|
||||
asm(value key self keyLen) "DICTSET";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
|
||||
asm(value key self keyLen) "DICTISETREF";
|
||||
|
||||
@pure
|
||||
fun uDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
|
||||
asm(value key self keyLen) "DICTUSETREF";
|
||||
|
||||
@pure
|
||||
fun sDictSetRef(mutate self: cell, keyLen: int, key: slice, value: cell): void
|
||||
asm(value key self keyLen) "DICTSETREF";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int
|
||||
asm(value key self keyLen) "DICTIADD";
|
||||
|
||||
@pure
|
||||
fun uDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int
|
||||
asm(value key self keyLen) "DICTUADD";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int
|
||||
asm(value key self keyLen) "DICTIREPLACE";
|
||||
|
||||
@pure
|
||||
fun uDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int
|
||||
asm(value key self keyLen) "DICTUREPLACE";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetRef(self: cell, keyLen: int, key: int): (cell, int)
|
||||
asm(key self keyLen) "DICTIGETREF" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun uDictGetRef(self: cell, keyLen: int, key: int): (cell, int)
|
||||
asm(key self keyLen) "DICTUGETREF" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun sDictGetRef(self: cell, keyLen: int, key: slice): (cell, int)
|
||||
asm(key self keyLen) "DICTGETREF" "NULLSWAPIFNOT";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
|
||||
asm(key self keyLen) "DICTIGETOPTREF";
|
||||
|
||||
@pure
|
||||
fun uDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
|
||||
asm(key self keyLen) "DICTUGETOPTREF";
|
||||
|
||||
@pure
|
||||
fun sDictGetRefOrNull(self: cell, keyLen: int, key: slice): cell
|
||||
asm(key self keyLen) "DICTGETOPTREF";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictDelete(mutate self: cell, keyLen: int, key: int): int
|
||||
asm(key self keyLen) "DICTIDEL";
|
||||
|
||||
@pure
|
||||
fun uDictDelete(mutate self: cell, keyLen: int, key: int): int
|
||||
asm(key self keyLen) "DICTUDEL";
|
||||
|
||||
@pure
|
||||
fun sDictDelete(mutate self: cell, keyLen: int, key: slice): int
|
||||
asm(key self keyLen) "DICTDEL";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int)
|
||||
asm(value key self keyLen) "DICTISETGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun uDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int)
|
||||
asm(value key self keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun sDictSetAndGet(mutate self: cell, keyLen: int, key: slice, value: slice): (slice, int)
|
||||
asm(value key self keyLen) "DICTSETGET" "NULLSWAPIFNOT";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
|
||||
asm(value key self keyLen) "DICTISETGETOPTREF";
|
||||
|
||||
@pure
|
||||
fun uDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
|
||||
asm(value key self keyLen) "DICTUSETGETOPTREF";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int)
|
||||
asm(key self keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun uDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int)
|
||||
asm(key self keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun sDictDeleteAndGet(mutate self: cell, keyLen: int, key: slice): (slice, int)
|
||||
asm(key self keyLen) "DICTDELGET" "NULLSWAPIFNOT";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
|
||||
asm(value key self keyLen) "DICTISETB";
|
||||
|
||||
@pure
|
||||
fun uDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
|
||||
asm(value key self keyLen) "DICTUSETB";
|
||||
|
||||
@pure
|
||||
fun sDictSetBuilder(mutate self: cell, keyLen: int, key: slice, value: builder): void
|
||||
asm(value key self keyLen) "DICTSETB";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int
|
||||
asm(value key self keyLen) "DICTIADDB";
|
||||
|
||||
@pure
|
||||
fun uDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int
|
||||
asm(value key self keyLen) "DICTUADDB";
|
||||
|
||||
@pure
|
||||
fun iDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int
|
||||
asm(value key self keyLen) "DICTIREPLACEB";
|
||||
|
||||
@pure
|
||||
fun uDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int
|
||||
asm(value key self keyLen) "DICTUREPLACEB";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int)
|
||||
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int)
|
||||
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (slice, slice, int)
|
||||
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int)
|
||||
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int)
|
||||
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictDeleteLastAndGet(mutate self: cell, keyLen: int): (slice, slice, int)
|
||||
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetFirst(self: cell, keyLen: int): (int, slice, int)
|
||||
asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetFirst(self: cell, keyLen: int): (int, slice, int)
|
||||
asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictGetFirst(self: cell, keyLen: int): (slice, slice, int)
|
||||
asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun iDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, int)
|
||||
asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, int)
|
||||
asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictGetFirstAsRef(self: cell, keyLen: int): (slice, cell, int)
|
||||
asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetLast(self: cell, keyLen: int): (int, slice, int)
|
||||
asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetLast(self: cell, keyLen: int): (int, slice, int)
|
||||
asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictGetLast(self: cell, keyLen: int): (slice, slice, int)
|
||||
asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun iDictGetLastAsRef(self: cell, keyLen: int): (int, cell, int)
|
||||
asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetLastAsRef(self: cell, keyLen: int): (int, cell, int)
|
||||
asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictGetLastAsRef(self: cell, keyLen: int): (slice, cell, int)
|
||||
asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun iDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun iDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
/**
|
||||
Prefix dictionary primitives.
|
||||
*/
|
||||
|
||||
@pure
|
||||
fun prefixDictGet(self: cell, keyLen: int, key: slice): (slice, slice, slice, int)
|
||||
asm(key self keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun prefixDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): int
|
||||
asm(value key self keyLen) "PFXDICTSET";
|
||||
|
||||
@pure
|
||||
fun prefixDictDelete(mutate self: cell, keyLen: int, key: slice): int
|
||||
asm(key self keyLen) "PFXDICTDEL";
|
25
crypto/smartcont/tolk-stdlib/tvm-lowlevel.tolk
Normal file
25
crypto/smartcont/tolk-stdlib/tvm-lowlevel.tolk
Normal file
|
@ -0,0 +1,25 @@
|
|||
// A part of standard library for Tolk
|
||||
tolk 0.6
|
||||
|
||||
/// Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls.
|
||||
/// The primitive returns the current value of `c3`.
|
||||
@pure
|
||||
fun getTvmRegisterC3(): continuation
|
||||
asm "c3 PUSH";
|
||||
|
||||
/// Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time.
|
||||
/// Note that after execution of this primitive the current code
|
||||
/// (and the stack of recursive function calls) won't change,
|
||||
/// but any other function call will use a function from the new code.
|
||||
fun setTvmRegisterC3(c: continuation): void
|
||||
asm "c3 POP";
|
||||
|
||||
/// Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist.
|
||||
@pure
|
||||
fun transformSliceToContinuation(s: slice): continuation
|
||||
asm "BLESS";
|
||||
|
||||
/// Moves a variable or a value [x] to the top of the stack.
|
||||
@pure
|
||||
fun stackMoveToTop<X>(mutate self: X): void
|
||||
asm "NOP";
|
Loading…
Add table
Add a link
Reference in a new issue