diff --git a/docs/contribute/tutorials/sample-tutorial.md b/docs/contribute/tutorials/sample-tutorial.md index 76d4f598a2..28e4eba692 100644 --- a/docs/contribute/tutorials/sample-tutorial.md +++ b/docs/contribute/tutorials/sample-tutorial.md @@ -63,7 +63,7 @@ Any technology that needs to be installed **prior** to starting the tutorial and - \```javascript *or* ```js can be used for any JavaScript code. - \```typescript or ```ts can be used for any TypeScript code. - \```jsx is for ReactJS code. - - \```cpp is for Func code. + - \```func is for FunC code. - Use \```graphql when highlighting GraphQL syntax. - Use \```json when highlighting valid JSON. (For invalid JSON examples use \```text instead.) - \```bash should *only* be used in code blocks where you need to have # style comments. This must be done carefully because in many situations the # character will render as a markdown heading. Typically, the Table of Contents will be affected if this occurs. @@ -117,4 +117,4 @@ This section ***must*** be present if you have taken any help in writing this tu Credit sources by adding their name and a link to the document when possible. -If it is not a digital document, include an ISBN or other form of reference. \ No newline at end of file +If it is not a digital document, include an ISBN or other form of reference. diff --git a/docs/develop/dapps/asset-processing/nfts.md b/docs/develop/dapps/asset-processing/nfts.md index 4e8c1ac97b..5c1ab152fb 100644 --- a/docs/develop/dapps/asset-processing/nfts.md +++ b/docs/develop/dapps/asset-processing/nfts.md @@ -19,7 +19,7 @@ Because each NFT makes use of its own smart contract, it is not possible to obta ### NFT Collections NFT Collection is a contract that serves to index and store NFT content and should contain the following interfaces: #### Get method `get_collection_data` -``` +```func (int next_item_index, cell collection_content, slice owner_address) get_collection_data() ``` Retrieves general information about collection, which represented with the following: @@ -28,13 +28,13 @@ Retrieves general information about collection, which represented with the follo 3. `owner_address` - a slice that contains the collection owner's address (this value can also be empty). #### Get method `get_nft_address_by_index` -``` +```func (slice nft_address) get_nft_address_by_index(int index) ``` This method can be used to verify the authenticity of an NFT and confirm whether it truly belongs to a specific collection. It also enables users to retrieve the address of an NFT by providing its index in the collection. The method should return a slice containing the address of the NFT that corresponds to the provided index. #### Get method `get_nft_content` -``` +```func (cell full_content) get_nft_content(int index, cell individual_content) ``` Since the collection serves as a common data storage for NFTs, this method is necessary to complete the NFT content. To use this method, first, it’s necessary to obtain the NFT’s `individual_content` by calling the corresponding `get_nft_data()` method. After obtaining the `individual_content`, it’s possible to call the `get_nft_content()` method with the NFT index and the `individual_content` cell. The method should return a TEP-64 cell containing the full content of the NFT. @@ -43,12 +43,12 @@ Since the collection serves as a common data storage for NFTs, this method is ne Basic NFTs should implement: #### Get method `get_nft_data()` -``` +```func (int init?, int index, slice collection_address, slice owner_address, cell individual_content) get_nft_data() ``` #### Inline message handler for `transfer` -``` +```tlb transfer#5fcc3d14 query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress custom_payload:(Maybe ^Cell) forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) = InternalMsgBody ``` Let's look at each parameter you need to fill in your message: @@ -182,19 +182,19 @@ Now that we’ve covered the basics of sending and receiving NFTs, let’s explo In this example, the NFT transfer message is found on [line 67](https://www.google.com/url?q=https://github.com/ton-blockchain/token-contract/blob/1ad314a98d20b41241d5329e1786fc894ad811de/nft/nft-sale.fc%23L67&sa=D&source=docs&ust=1685436161341866&usg=AOvVaw1yuoIzcbEuvqMS4xQMqfXE): -``` +```func var nft_msg = begin_cell() .store_uint(0x18, 6) .store_slice(nft_address) .store_coins(0) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) .store_uint(op::transfer(), 32) .store_uint(query_id, 64) - .store_slice(sender_address) ;; new_owner_address - .store_slice(sender_address) ;; response_address - .store_int(0, 1) ;; empty custom_payload - .store_coins(0) ;; forward amount to new_owner_address - .store_int(0, 1); ;; empty forward_payload + .store_slice(sender_address) // new_owner_address + .store_slice(sender_address) // response_address + .store_int(0, 1) // empty custom_payload + .store_coins(0) // forward amount to new_owner_address + .store_int(0, 1); // empty forward_payload send_raw_message(nft_msg.end_cell(), 128 + 32); @@ -206,8 +206,8 @@ Let's examine each line of code: - `store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)` - the remaining components that make up the message header are left empty. - `store_uint(op::transfer(), 32)` - this is the start of the msg_body. Here we start by using the transfer OP code so the receiver understands its transfer ownership message. - `store_uint(query_id, 64)` - store query_id -- `store_slice(sender_address) ;; new_owner_address` - the first stored address is the address used for transferring NFTs and sending notifications. -- `store_slice(sender_address) ;; response_address` - the second stored address is a response address. +- `store_slice(sender_address) // new_owner_address` - the first stored address is the address used for transferring NFTs and sending notifications. +- `store_slice(sender_address) // response_address` - the second stored address is a response address. - `store_int(0, 1)` - the custom payload flag is set to 0, indicating there is no custom payload required. - `store_coins(0)` - amount of TON to be forwarded with the message. In this example it’s set to 0, however, it is recommended to set this value to a higher amount (such as at least 0.01 TON) in order to create a forward message and notify the new owner that they have received the NFT. The amount should be sufficient to cover any associated fees and costs. - `.store_int(0, 1)` - custom payload flag. It's necessary to set up to `1` if your service should pass payload as a ref. @@ -215,11 +215,11 @@ Let's examine each line of code: ### Receiving NFTs Once we've sent the NFT, it is critical to determine when it has been received by the new owner. A good example of how to do this can be found in the same NFT sale smart contract: -``` +```func slice cs = in_msg_full.begin_parse(); int flags = cs~load_uint(4); -if (flags & 1) { ;; ignore all bounced messages +if (flags & 1) { // ignore all bounced messages return (); } slice sender_address = cs~load_msg_addr(); @@ -233,7 +233,7 @@ Let's again examine each line of code: - `slice cs = in_msg_full.begin_parse();` - used to parse the incoming message. - `int flags = cs~load_uint(4);` - used to load flags from the first 4 bits of the message. -- `if (flags & 1) { return (); } ;; ignore all bounced messages` - used to verify that the message has not bounced. It’s important to carry out this process for all your incoming messages if there is no reason to do otherwise. Bounced messages are messages that encountered errors while trying to receive a transaction and were returned to the sender. +- `if (flags & 1) { return (); } // ignore all bounced messages` - used to verify that the message has not bounced. It’s important to carry out this process for all your incoming messages if there is no reason to do otherwise. Bounced messages are messages that encountered errors while trying to receive a transaction and were returned to the sender. - `slice sender_address = cs~load_msg_addr();` - next the message sender is loaded. In this case specifically by using an NFT address. - `throw_unless(500, equal_slices(sender_address, nft_address));` - used to verify that the sender is indeed an NFT that should have been transferred via a contract. It's quite difficult to parse NFT data from smart contracts, so in most cases the NFT address is predefined at contract creation. - `int op = in_msg_body~load_uint(32);` - loads message OP code. diff --git a/docs/develop/dapps/tutorials/simple-zk-on-ton.md b/docs/develop/dapps/tutorials/simple-zk-on-ton.md index b8576a534d..35a62f6b31 100644 --- a/docs/develop/dapps/tutorials/simple-zk-on-ton.md +++ b/docs/develop/dapps/tutorials/simple-zk-on-ton.md @@ -246,7 +246,7 @@ The above lines are the new [TVM opcodes](https://docs.ton.org/learn/tvm-instruc The load_data and save_data functions are simply used to load and save the proof verification results (only for test purposes). ```func -() load_data() impure { +() load_data() { var ds = get_data().begin_parse(); @@ -255,7 +255,7 @@ The load_data and save_data functions are simply used to load and save the proof ds.end_parse(); } -() save_data() impure { +() save_data() { set_data( begin_cell() .store_uint(ctx_res, 32) @@ -266,15 +266,15 @@ The load_data and save_data functions are simply used to load and save the proof Next there are several simple util functions that are used to load the proof data sent to the contract: ```func -(slice, slice) load_p1(slice body) impure { +(slice, slice) load_p1(slice body) { ... } -(slice, slice) load_p2(slice body) impure { +(slice, slice) load_p2(slice body) { ... } -(slice, int) load_newint(slice body) impure { +(slice, int) load_newint(slice body) { ... } ``` @@ -288,7 +288,7 @@ And the last part is the groth16Verify function which is required to check the v int pubInput0 -) impure { +) { slice cpub = bls_g1_multiexp( @@ -306,7 +306,7 @@ And the last part is the groth16Verify function which is required to check the v pi_c, vk_delta_2, vk_alpha_1, vk_beta_2, 4); - ;; ctx_res = a; + // ctx_res = a; if (a == 0) { ctx_res = 0; } else { diff --git a/docs/develop/data-formats/library-cells.md b/docs/develop/data-formats/library-cells.md index a9cd18e63c..2915f471ea 100644 --- a/docs/develop/data-formats/library-cells.md +++ b/docs/develop/data-formats/library-cells.md @@ -26,7 +26,7 @@ You can consider library cell as C++ pointer: one small cell that points to larg Library cell is [exotic cell](/develop/data-formats/exotic-cells) that contains a reference to some other static cell. In particular it contains 256 bit of hash of referenced cell. -For TVM, library cells works as follows: whenever TVM receives a command to open a cell to a slice (TVM Instruction: `CTOS`, funC method: `.begin_parse()`), it searches cell with the corresponding hash from library cell in the Masterchain library context. If found it, it opens referenced cell and returns its slice. +For TVM, library cells works as follows: whenever TVM receives a command to open a cell to a slice (TVM Instruction: `CTOS`, FunC method: `.begin_parse()`), it searches cell with the corresponding hash from library cell in the Masterchain library context. If found it, it opens referenced cell and returns its slice. Opening library cell costs the same as opening ordinar cell, so it can be used as transparent replacement for static cells that however occupy much less space (and thus costs less fees for storage and sending). @@ -50,11 +50,11 @@ Basically you need to put tag and hash to the builder and then "close builder as It can be done in Fift-asm construction like [this](https://github.com/ton-blockchain/multisig-contract-v2/blob/master/contracts/auto/order_code.func), example of compilation some contract directly to library cell [here](https://github.com/ton-blockchain/multisig-contract-v2/blob/master/wrappers/Order.compile.ts). -```fift -;; https://docs.ton.org/tvm.pdf, page 30 -;; Library reference cell — Always has level 0, and contains 8+256 data bits, including its 8-bit type integer 2 -;; and the representation hash Hash(c) of the library cell being referred to. When loaded, a library -;; reference cell may be transparently replaced by the cell it refers to, if found in the current library context. +```func +// https://docs.ton.org/tvm.pdf, page 30 +// Library reference cell — Always has level 0, and contains 8+256 data bits, including its 8-bit type integer 2 +// and the representation hash Hash(c) of the library cell being referred to. When loaded, a library +// reference cell may be transparently replaced by the cell it refers to, if found in the current library context. cell order_code() asm "spec PUSHREF"; ``` diff --git a/docs/develop/data-formats/msg-tlb.mdx b/docs/develop/data-formats/msg-tlb.mdx index d5bea6e61b..8c602adcdb 100644 --- a/docs/develop/data-formats/msg-tlb.mdx +++ b/docs/develop/data-formats/msg-tlb.mdx @@ -340,29 +340,29 @@ var_int$_ {n:#} len:(#< n) value:(int (len * 8)) ## Message example -### Regular func internal message +### Regular FunC internal message ```func var msg = begin_cell() - .store_uint(0, 1) ;; tag - .store_uint(1, 1) ;; ihr_disabled - .store_uint(1, 1) ;; allow bounces - .store_uint(0, 1) ;; not bounced itself + .store_uint(0, 1) // tag + .store_uint(1, 1) // ihr_disabled + .store_uint(1, 1) // allow bounces + .store_uint(0, 1) // not bounced itself .store_slice(source) .store_slice(destination) - ;; serialize CurrencyCollection (see below) + // serialize CurrencyCollection (see below) .store_coins(amount) .store_dict(extra_currencies) - .store_coins(0) ;; ihr_fee - .store_coins(fwd_value) ;; fwd_fee - .store_uint(cur_lt(), 64) ;; lt of transaction - .store_uint(now(), 32) ;; unixtime of transaction - .store_uint(0, 1) ;; no init-field flag (Maybe) - .store_uint(0, 1) ;; inplace message body flag (Either) + .store_coins(0) // ihr_fee + .store_coins(fwd_value) // fwd_fee + .store_uint(cur_lt(), 64) // lt of transaction + .store_uint(now(), 32) // unixtime of transaction + .store_uint(0, 1) // no init-field flag (Maybe) + .store_uint(0, 1) // inplace message body flag (Either) .store_slice(msg_body) .end_cell(); ``` -### Regular func message in short form +### Regular FunC message in short form Message parts that are always overwritten by validators could be skipped(fill with zero bits). Message's sender here skipped too, serialized as `addr_none$00`. diff --git a/docs/develop/fift/fift-and-tvm-assembly.md b/docs/develop/fift/fift-and-tvm-assembly.md index e40147a08d..0e6bd15acc 100644 --- a/docs/develop/fift/fift-and-tvm-assembly.md +++ b/docs/develop/fift/fift-and-tvm-assembly.md @@ -6,7 +6,7 @@ Fift is stack-based programming language that has TON-specific features and ther Fift is executed **at compile-time** - when your compiler builds smart-contract code BOC, after FunC code is processed. Fift can look differently: -``` +```fift // tuple primitives x{6F0} @Defop(4u) TUPLE x{6F00} @Defop NIL @@ -15,7 +15,7 @@ x{6F02} dup @Defop PAIR @Defop CONS ``` > TVM opcode definitions in Asm.fif -``` +```fift "Asm.fif" include <{ SETCP0 DUP IFNOTRET // return if recv_internal DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods @@ -66,12 +66,12 @@ contract: ``` Put the BOC in `fift/blob.boc`, then add the following code to `fift/blob.fif`: -``` +```fift B B>boc ref, b> s PUSHSLICE"; ``` The reason is obvious: Fift is doing calculations in compile-time, where no `x` is yet available for conversion. To convert non-constant integer to string slice, you need TVM assembly. For example, this is code by one of TON Smart Challenge 3 participants': -``` +```func tuple digitize_number(int value) asm "NIL WHILE:<{ OVER }>DO<{ SWAP TEN DIVMOD s1 s2 XCHG TPUSH }> NIP"; @@ -106,16 +106,16 @@ builder store_signed(builder msg, int v) inline_ref { ### [TVM assembly] - Cheap modulo multiplication -``` -int mul_mod(int a, int b, int m) inline_ref { ;; 1232 gas units +```func +int mul_mod(int a, int b, int m) inline_ref { // 1232 gas units (_, int r) = muldivmod(a % m, b % m, m); return r; } -int mul_mod_better(int a, int b, int m) inline_ref { ;; 1110 gas units +int mul_mod_better(int a, int b, int m) inline_ref { // 1110 gas units (_, int r) = muldivmod(a, b, m); return r; } -int mul_mod_best(int a, int b, int m) asm "x{A988} s,"; ;; 65 gas units +int mul_mod_best(int a, int b, int m) asm "x{A988} s,"; // 65 gas units ``` `x{A988}` is opcode formatted according to [5.2 Division](/learn/tvm-instructions/instructions#52-division): division with pre-multiplication, where the only returned result is remainder modulo third argument. But opcode needs to get into smart-contract code - that's what `s,` does: it stores slice on top of stack into builder slightly below. diff --git a/docs/develop/func/builtins.md b/docs/develop/func/builtins.md deleted file mode 100644 index 5290fc2f75..0000000000 --- a/docs/develop/func/builtins.md +++ /dev/null @@ -1,24 +0,0 @@ -# Built-ins -This section describes some language constructions which are less fundamental than the ones described in previous articles. They could be defined in [stdlib.fc](/develop/func/stdlib) but it would leave less room for the FunC optimizer. - -## Throwing exceptions -Exceptions can be thrown by conditional primitives `throw_if`, and `throw_unless`, and by unconditional `throw`. The first argument is the error code; the second is the condition (`throw` has only one argument). These primitives have parametrized versions `throw_arg_if`, `throw_arg_unless`, and `throw_arg`. The first argument is the exception parameter of any type; the second is the error code; the third is the condition (`throw_arg` has only two arguments). - -## Booleans -- `true` is alias for `-1` -- `false` is alias for `0` - -## Dump variable -A variable can be dumped to the debug log by the `~dump` function. - -## Dump string -A string can be dumped to the debug log by the `~strdump` function. - -## Integer operations -- `muldiv` is a multiple-then-divide operation. The intermediate result is stored in 513-bit integer, so it won't overflow if the actual result fits into a 257-bit integer. -- `divmod` is a operation that takes two numbers as parameters and gives the quotient and remainder of their division. - -## Other primitives -- `null?` checks whether the argument is `null`. By the value `null` of a TVM type, `Null` FunC represents absence of a value of some atomic type; see [null values](/develop/func/types#null-values). -- `touch` and `~touch` move a variable to the top of the stack -- `at` gets the value of a tuple component on the specified position diff --git a/docs/develop/func/changelog.md b/docs/develop/func/changelog.md index 7c4c480f66..bd28332fb9 100644 --- a/docs/develop/func/changelog.md +++ b/docs/develop/func/changelog.md @@ -34,17 +34,36 @@ Released in [10.2022 update](https://github.com/ton-blockchain/ton/releases/tag/ In this version were added: - [Multiline asms](/develop/func/functions#multiline-asms) - Duplication of identical definition for constants and asms became allowed -- Bitwise operations for constants for constants became allowed +- Bitwise operations for constants became allowed # Version 0.4.0 Released in [01.2023 update](https://github.com/ton-blockchain/ton/releases/tag/v2023.01). In this version were added: - [try/catch statements](/develop/func/statements#try-catch-statements) -- [throw_arg functions](/develop/func/builtins#throwing-exceptions) +- [throw_arg functions](/develop/func/stdlib#throwing-exceptions) - allowed in-place modification and mass-assignments of global variables: `a~inc()` and `(a, b) = (3, 5)`, where `a` is global Fixed: - forbidden ambiguous modification of local variables after it's usage in the same expression: `var x = (ds, ds~load_uint(32), ds~load_unit(64));` are forbidden, while `var x = (ds~load_uint(32), ds~load_unit(64), ds);` are not - Allowed empty inline functions - fix rare `while` optimization bug + +# Version 0.5.0 +Released in June 2024. + +This update was focused on syntax changes and additions. +1. Traditional comment syntax `//` and `/*` is now supported (and preferred), block comments are no longer nested +2. All functions are impure by default. Keyword `impure` has become deprecated, but its antonym keyword `pure` is introduced +3. Keyword `method_id` is deprecated, also. It was replaced as too obscure. Now there is `get`, written on the left: `get int seqno() { ... }` +4. Pragmas `compute-asm-ltr` and `allow-post-modification` are deprecated (always on) +5. FunC compiler auto-inlines simple function wrappers, it's a kernel for potential camelCase and stdlib renamings +6. FunC compiler can drop unused functions from Fift output, activated by `#pragma remove-unused-functions` +7. Changed priorities of operators `& | ^` to more intuitive ones +8. Built-in functions are also placed into stdlib +9. Tremendously enhanced internal framework for testing FunC, a basis for future more radical language improvements +10. Some bug fixes, for wasm/Tact in particular +11. IDE plugin for JetBrains has been updated, it supports new syntax and introduces a setting "FunC language level", encoupled with inspections to remove `impure`, replace `method_id` with `get`, etc. +12. IDE plugin for VS Code has also been updated in the same manner: it supports new syntax, has the "FunC language level" setting and related diagnostics/quickfixes + +See [migration guide](/develop/func/migration-guide). diff --git a/docs/develop/func/comments.md b/docs/develop/func/comments.md index a83cda7d3c..3abb24e053 100644 --- a/docs/develop/func/comments.md +++ b/docs/develop/func/comments.md @@ -1,29 +1,16 @@ # Comments -FunC has single-line comments which start with `;;` (double `;`). For example: -```func -int x = 1; ;; assign 1 to x -``` -It also has multi-line comments which start with `{-` and end with `-}`. Note that unlike in many other languages, FunC multi-line comments can be nested. For example: +FunC has traditional comments: `//` for single-line and `/* ... */` for multi-line: ```func -{- This is a multi-line comment - {- this is a comment in the comment -} --} +/* + Just an assignment +*/ +int x = 1; // assign 1 to x ``` -Moreover, there can be one-line comments inside multi-line ones, and one-line comments `;;` are "stronger" than multiline `{- -}`. In other words in the following example: - -```func -{- - Start of the comment - -;; this comment ending is itself commented -> -} - -const a = 10; -;; this comment begining is itself commented -> {- - - End of the comment --} -``` +They are not nested, like in C, JavaScript, and many other languages. -`const a = 10;` is inside multiline comment and is commented out. +:::caution +Before v0.5.0, FunC had Lisp-style comments (`;;` and `{- ... -}`). +Although they are still supported, they might be removed in future versions. +::: diff --git a/docs/develop/func/compiler_directives.md b/docs/develop/func/compiler_directives.md index 38e34c4376..96fc946119 100644 --- a/docs/develop/func/compiler_directives.md +++ b/docs/develop/func/compiler_directives.md @@ -44,38 +44,13 @@ The syntax of this pragma is the same as the version pragma but it fails if the It can be used for blacklisting a specific version known to have problems, for example. -### #pragma allow-post-modification -_funC v0.4.1_ +### #pragma remove-unused-functions -By default it is prohibited to use variable prior to its modification in the same expression. In other words, expression `(x, y) = (ds, ds~load_uint(8))` won't compile, while `(x, y) = (ds~load_uint(8), ds)` is valid. +_FunC v0.5.0_ -This rule can be overwritten, by `#pragma allow-post-modification`, which allow to modify variable after usage in mass assignments and function invocation; as usual sub-expressions will be computed left to right: `(x, y) = (ds, ds~load_bits(8))` will result in `x` containing initial `ds`; `f(ds, ds~load_bits(8))` first argument of `f` will contain initial `ds`, and second - 8 bits of `ds`. +Forces FunC to drop unused functions from fift output. Probably, will be always on in the future. -`#pragma allow-post-modification` works only for code after the pragma. +### Deprecated pragmas -### #pragma compute-asm-ltr -_funC v0.4.1_ - -Asm declarations can overwrite order of arguments, for instance in the following expression - -```func -idict_set_ref(ds~load_dict(), ds~load_uint(8), ds~load_uint(256), ds~load_ref()) -``` - -order of parsing will be: `load_ref()`, `load_uint(256)`, `load_dict()` and `load_uint(8)` due to following asm declaration (note `asm(value index dict key_len)`): - -```func -cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; -``` - -This behavior can be changed to strict left-to-right order of computation via `#pragma compute-asm-ltr` - -As a result in -```func -#pragma compute-asm-ltr -... -idict_set_ref(ds~load_dict(), ds~load_uint(8), ds~load_uint(256), ds~load_ref()); -``` -order of parsing will be `load_dict()`, `load_uint(8)`, `load_uint(256)`, `load_ref()` and all asm permutation will happen after computation. - -`#pragma compute-asm-ltr` works only for code after the pragma. +* `allow-post-modification` is deprecated since v0.5.0, now always on +* `compute-asm-ltr` is deprecated since v0.5.0, now always on diff --git a/docs/develop/func/cookbook.md b/docs/develop/func/cookbook.md index 2ec0d3f51e..893212ecdc 100644 --- a/docs/develop/func/cookbook.md +++ b/docs/develop/func/cookbook.md @@ -11,13 +11,13 @@ Compared to the [FunC Documentation](/develop/func/types), this article is more Let's say we want to check if any event is relevant. To do this, we use the flag variable. Remember that in FunC `true` is `-1` and `false` is `0`. ```func -int flag = 0; ;; false +int flag = 0; // false if (flag) { - ;; do something + // do something } else { - ;; reject the transaction + // reject the transaction } ``` @@ -39,7 +39,6 @@ int multiplier = number; int degree = 5; repeat(degree - 1) { - number *= multiplier; } ``` @@ -53,19 +52,19 @@ repeat(degree - 1) { While is useful when we do not know how often to perform a particular action. For example, take a `cell`, which is known to store up to four references to other cells. ```func -cell inner_cell = begin_cell() ;; create a new empty builder - .store_uint(123, 16) ;; store uint with value 123 and length 16 bits - .end_cell(); ;; convert builder to a cell +cell inner_cell = begin_cell() // create a new empty builder + .store_uint(123, 16) // store uint with value 123 and length 16 bits + .end_cell(); // convert builder to a cell cell message = begin_cell() - .store_ref(inner_cell) ;; store cell as reference + .store_ref(inner_cell) // store cell as reference .store_ref(inner_cell) .end_cell(); -slice msg = message.begin_parse(); ;; convert cell to slice -while (msg.slice_refs_empty?() != -1) { ;; we should remind that -1 is true - cell inner_cell = msg~load_ref(); ;; load cell from slice msg - ;; do something +slice msg = message.begin_parse(); // convert cell to slice +while (msg.slice_refs_empty?() != -1) { // we should remind that -1 is true + cell inner_cell = msg~load_ref(); // load cell from slice msg + // do something } ``` @@ -93,8 +92,8 @@ When we need the cycle to run at least once, we use `do until`. int flag = 0; do { - ;; do something even flag is false (0) -} until (flag == -1); ;; -1 is true + // do something even flag is false (0) +} until (flag == -1); // -1 is true ``` > 💡 Useful links @@ -106,31 +105,31 @@ do { Before working with `slice`, it is necessary to check whether it has any data to process it correctly. We can use `slice_empty?()` to do this, but we have to consider that it will return `-1` (`true`) if there is at least one `bit` of data or one `ref`. ```func -;; creating empty slice +// creating empty slice slice empty_slice = ""; -;; `slice_empty?()` returns `true`, because slice doesn't have any `bits` and `refs` +// `slice_empty?()` returns `true`, because slice doesn't have any `bits` and `refs` empty_slice.slice_empty?(); -;; creating slice which contains bits only +// creating slice which contains bits only slice slice_with_bits_only = "Hello, world!"; -;; `slice_empty?()` returns `false`, because slice have any `bits` +// `slice_empty?()` returns `false`, because slice have any `bits` slice_with_bits_only.slice_empty?(); -;; creating slice which contains refs only +// creating slice which contains refs only slice slice_with_refs_only = begin_cell() .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_empty?()` returns `false`, because slice have any `refs` +// `slice_empty?()` returns `false`, because slice have any `refs` slice_with_refs_only.slice_empty?(); -;; creating slice which contains bits and refs +// creating slice which contains bits and refs slice slice_with_bits_and_refs = begin_cell() .store_slice("Hello, world!") .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_empty?()` returns `false`, because slice have any `bits` and `refs` +// `slice_empty?()` returns `false`, because slice have any `bits` and `refs` slice_with_bits_and_refs.slice_empty?(); ``` > 💡 Useful links @@ -153,31 +152,31 @@ slice_with_bits_and_refs.slice_empty?(); If we need to check only the `bits` and it does not matter if there are any `refs` in `slice`, then we should use `slice_data_empty?()`. ```func -;; creating empty slice +// creating empty slice slice empty_slice = ""; -;; `slice_data_empty?()` returns `true`, because slice doesn't have any `bits` +// `slice_data_empty?()` returns `true`, because slice doesn't have any `bits` empty_slice.slice_data_empty?(); -;; creating slice which contains bits only +// creating slice which contains bits only slice slice_with_bits_only = "Hello, world!"; -;; `slice_data_empty?()` returns `false`, because slice have any `bits` +// `slice_data_empty?()` returns `false`, because slice have any `bits` slice_with_bits_only.slice_data_empty?(); -;; creating slice which contains refs only +// creating slice which contains refs only slice slice_with_refs_only = begin_cell() .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_data_empty?()` returns `true`, because slice doesn't have any `bits` +// `slice_data_empty?()` returns `true`, because slice doesn't have any `bits` slice_with_refs_only.slice_data_empty?(); -;; creating slice which contains bits and refs +// creating slice which contains bits and refs slice slice_with_bits_and_refs = begin_cell() .store_slice("Hello, world!") .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_data_empty?()` returns `false`, because slice have any `bits` +// `slice_data_empty?()` returns `false`, because slice have any `bits` slice_with_bits_and_refs.slice_data_empty?(); ``` @@ -201,31 +200,31 @@ slice_with_bits_and_refs.slice_data_empty?(); In case we are only interested in `refs`, we should check their presence using `slice_refs_empty?()`. ```func -;; creating empty slice +// creating empty slice slice empty_slice = ""; -;; `slice_refs_empty?()` returns `true`, because slice doesn't have any `refs` +// `slice_refs_empty?()` returns `true`, because slice doesn't have any `refs` empty_slice.slice_refs_empty?(); -;; creating slice which contains bits only +// creating slice which contains bits only slice slice_with_bits_only = "Hello, world!"; -;; `slice_refs_empty?()` returns `true`, because slice doesn't have any `refs` +// `slice_refs_empty?()` returns `true`, because slice doesn't have any `refs` slice_with_bits_only.slice_refs_empty?(); -;; creating slice which contains refs only +// creating slice which contains refs only slice slice_with_refs_only = begin_cell() .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_refs_empty?()` returns `false`, because slice have any `refs` +// `slice_refs_empty?()` returns `false`, because slice have any `refs` slice_with_refs_only.slice_refs_empty?(); -;; creating slice which contains bits and refs +// creating slice which contains bits and refs slice slice_with_bits_and_refs = begin_cell() .store_slice("Hello, world!") .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_refs_empty?()` returns `false`, because slice have any `refs` +// `slice_refs_empty?()` returns `false`, because slice have any `refs` slice_with_bits_and_refs.slice_refs_empty?(); ``` @@ -253,15 +252,15 @@ cell cell_with_bits_and_refs = begin_cell() .store_ref(null()) .end_cell(); -;; Change `cell` type to slice with `begin_parse()` +// Change `cell` type to slice with `begin_parse()` slice cs = cell_with_bits_and_refs.begin_parse(); -;; determine if slice is empty +// determine if slice is empty if (cs.slice_empty?()) { - ;; cell is empty + // cell is empty } else { - ;; cell is not empty + // cell is not empty } ``` @@ -286,11 +285,11 @@ cell d = new_dict(); d~udict_set(256, 0, "hello"); d~udict_set(256, 1, "world"); -if (d.dict_empty?()) { ;; Determine if dict is empty - ;; dict is empty +if (d.dict_empty?()) { // Determine if dict is empty + // dict is empty } else { - ;; dict is not empty + // dict is not empty } ``` @@ -307,7 +306,7 @@ else { When working with `tuples`, it is important always to know if any values are inside for extraction. If we try to extract value from an empty `tuple`, we get an error: "not a tuple of valid size" with `exit code 7`. ```func -;; Declare tlen function because it's not presented in stdlib +// Declare tlen function because it's not presented in stdlib (int) tlen (tuple t) asm "TLEN"; () main () { @@ -316,10 +315,10 @@ When working with `tuples`, it is important always to know if any values are ins t~tpush(37); if (t.tlen() == 0) { - ;; tuple is empty + // tuple is empty } else { - ;; tuple is not empty + // tuple is not empty } } ``` @@ -343,9 +342,9 @@ tuple numbers = null(); numbers = cons(100, numbers); if (numbers.null?()) { - ;; list-style list is empty + // list-style list is empty } else { - ;; list-style list is not empty + // list-style list is not empty } ``` @@ -356,19 +355,19 @@ We are adding number 100 to our list-style list with [cons](/develop/func/stdlib Let’s say we have a `counter` that stores the number of transactions. This variable is not available during the first transaction in the smart contract state, because the state is empty, so it is necessary to process such a case. If the state is empty, we create a variable `counter` and save it. ```func -;; `get_data()` will return the data cell from contract state +// `get_data()` will return the data cell from contract state cell contract_data = get_data(); slice cs = contract_data.begin_parse(); if (cs.slice_empty?()) { - ;; contract data is empty, so we create counter and save it + // contract data is empty, so we create counter and save it int counter = 1; - ;; create cell, add counter and save in contract state + // create cell, add counter and save in contract state set_data(begin_cell().store_uint(counter, 32).end_cell()); } else { - ;; contract data is not empty, so we get our counter, increase it and save - ;; we should specify correct length of our counter in bits + // contract data is not empty, so we get our counter, increase it and save + // we should specify correct length of our counter in bits int counter = cs~load_uint(32) + 1; set_data(begin_cell().store_uint(counter, 32).end_cell()); } @@ -393,21 +392,21 @@ else { If we want the contract to send an internal message, we should first properly create it as a cell, specifying the technical flags, the recipient address, and the rest data. ```func -;; We use literal `a` to get valid address inside slice from string containing address +// We use literal `a` to get valid address inside slice from string containing address slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; int amount = 1000000000; -;; we use `op` for identifying operations +// we use `op` for identifying operations int op = 0; cell msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) .store_uint(op, 32) .end_cell(); -send_raw_message(msg, 3); ;; mode 3 - pay fees separately and ignore errors +send_raw_message(msg, 3); // mode 3 - pay fees separately and ignore errors ``` > 💡 Noted @@ -439,11 +438,11 @@ In the body of a message that follows flags and other technical data, we can sen We can also send the body of the message inside the same `cell` as header, if we are sure that we have enough space. In this case, we need to set the bit to `0`. ```func -;; We use literal `a` to get valid address inside slice from string containing address +// We use literal `a` to get valid address inside slice from string containing address slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; int amount = 1000000000; int op = 0; -cell message_body = begin_cell() ;; Creating a cell with message +cell message_body = begin_cell() // Creating a cell with message .store_uint(op, 32) .store_slice("❤") .end_cell(); @@ -452,12 +451,12 @@ cell msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) ;; default message headers (see sending messages page) - .store_uint(1, 1) ;; set bit to 1 to indicate that the cell will go on + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) // default message headers (see sending messages page) + .store_uint(1, 1) // set bit to 1 to indicate that the cell will go on .store_ref(message_body) .end_cell(); -send_raw_message(msg, 3); ;; mode 3 - pay fees separately and ignore errors +send_raw_message(msg, 3); // mode 3 - pay fees separately and ignore errors ``` > 💡 Noted @@ -491,7 +490,7 @@ send_raw_message(msg, 3); ;; mode 3 - pay fees separately and ignore errors When sending messages, the body message can be sent either as `cell` or as `slice`. In this example, we send the body of the message inside the `slice`. ```func -;; We use literal `a` to get valid address inside slice from string containing address +// We use literal `a` to get valid address inside slice from string containing address slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; int amount = 1000000000; int op = 0; @@ -501,12 +500,12 @@ cell msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) .store_uint(op, 32) .store_slice(message_body) .end_cell(); -send_raw_message(msg, 3); ;; mode 3 - pay fees separately and ignore errors +send_raw_message(msg, 3); // mode 3 - pay fees separately and ignore errors ``` > 💡 Noted @@ -536,14 +535,14 @@ forall X -> (tuple) to_tuple (X x) asm "NOP"; int i = 0; while (i < len) { int x = t.at(i); - ;; do something with x + // do something with x i = i + 1; } i = len - 1; while (i >= 0) { int x = t.at(i); - ;; do something with x + // do something with x i = i - 1; } } @@ -561,7 +560,7 @@ When using any features we actually use pre-prepared for us methods inside `stdl For example, we have the method of `tpush`, which adds an element to `tuple`, but without `tpop`. In this case, we should do this: ```func -;; ~ means it is modifying method +// ~ means it is modifying method forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; ``` @@ -597,10 +596,10 @@ forall X -> tuple cast_to_tuple (X x) asm "NOP"; forall X -> int cast_to_int (X x) asm "NOP"; forall X -> (tuple) to_tuple (X x) asm "NOP"; -;; define global variable +// define global variable global int max_value; -() iterate_tuple (tuple t) impure { +() iterate_tuple (tuple t) { repeat (t.tuple_length()) { var value = t~tpop(); if (is_tuple(value)) { @@ -618,9 +617,9 @@ global int max_value; () main () { tuple t = to_tuple([[2,6], [1, [3, [3, 5]]], 3]); int len = t.tuple_length(); - max_value = 0; ;; reset max_value; - iterate_tuple(t); ;; iterate tuple and find max value - ~dump(max_value); ;; 6 + max_value = 0; // reset max_value; + iterate_tuple(t); // iterate tuple and find max value + ~dump(max_value); // 6 } ``` @@ -628,7 +627,7 @@ global int max_value; > > ["Global variables" in docs](/develop/func/global_variables) > -> ["~dump" in docs](/develop/func/builtins#dump-variable) +> ["~dump" in docs](/develop/func/stdlib#debug-primitives) > > ["TVM instructions" in docs](/learn/tvm-instructions/instructions) @@ -640,25 +639,25 @@ global int max_value; forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; () main () { - ;; creating an empty tuple + // creating an empty tuple tuple names = empty_tuple(); - ;; push new items + // push new items names~tpush("Naito Narihira"); names~tpush("Shiraki Shinichi"); names~tpush("Akamatsu Hachemon"); names~tpush("Takaki Yuichi"); - ;; pop last item + // pop last item slice last_name = names~tpop(); - ;; get first item + // get first item slice first_name = names.first(); - ;; get an item by index + // get an item by index slice best_name = names.at(2); - ;; getting the length of the list + // getting the length of the list int number_names = names.tlen(); } ``` @@ -679,37 +678,37 @@ forall X -> slice cast_to_slice (X x) asm "NOP"; forall X -> tuple cast_to_tuple (X x) asm "NOP"; forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; -forall X -> () resolve_type (X value) impure { - ;; value here is of type X, since we dont know what is the exact value - we would need to check what is the value and then cast it +forall X -> () resolve_type (X value) { + // value here is of type X, since we dont know what is the exact value - we would need to check what is the value and then cast it if (is_null(value)) { - ;; do something with the null + // do something with the null } elseif (is_int(value)) { int valueAsInt = cast_to_int(value); - ;; do something with the int + // do something with the int } elseif (is_slice(value)) { slice valueAsSlice = cast_to_slice(value); - ;; do something with the slice + // do something with the slice } elseif (is_cell(value)) { cell valueAsCell = cast_to_cell(value); - ;; do something with the cell + // do something with the cell } elseif (is_tuple(value)) { tuple valueAsTuple = cast_to_tuple(value); - ;; do something with the tuple + // do something with the tuple } } () main () { - ;; creating an empty tuple + // creating an empty tuple tuple stack = empty_tuple(); - ;; let's say we have tuple and do not know the exact types of them + // let's say we have tuple and do not know the exact types of them stack~tpush("Some text"); stack~tpush(4); - ;; we use var because we do not know type of value + // we use var because we do not know type of value var value = stack~tpop(); resolve_type(value); } @@ -726,7 +725,7 @@ forall X -> () resolve_type (X value) impure { int current_time = now(); if (current_time > 1672080143) { - ;; do some stuff + // do some stuff } ``` @@ -739,7 +738,7 @@ Checkout [Random Number Generation](https://docs.ton.org/develop/smart-contracts ::: ```func -randomize_lt(); ;; do this once +randomize_lt(); // do this once int a = rand(10); int b = rand(1000000); @@ -753,12 +752,12 @@ Note that xp+zp is a valid variable name ( without spaces between ). ```func (int) modulo_operations (int xp, int zp) { - ;; 2^255 - 19 is a prime number for montgomery curves, meaning all operations should be done against its prime + // 2^255 - 19 is a prime number for montgomery curves, meaning all operations should be done against its prime int prime = 57896044618658097711785492504343953926634992332820282019728792003956564819949; - ;; muldivmod handles the next two lines itself - ;; int xp+zp = (xp + zp) % prime; - ;; int xp-zp = (xp - zp + prime) % prime; + // muldivmod handles the next two lines itself + // int xp+zp = (xp + zp) % prime; + // int xp-zp = (xp - zp + prime) % prime; (_, int xp+zp*xp-zp) = muldivmod(xp + zp, xp - zp, prime); return xp+zp*xp-zp; } @@ -774,11 +773,11 @@ Note that xp+zp is a valid variable name ( without spaces between ). ```func int number = 198; -throw_if(35, number > 50); ;; the error will be triggered only if the number is greater than 50 +throw_if(35, number > 50); // the error will be triggered only if the number is greater than 50 -throw_unless(39, number == 198); ;; the error will be triggered only if the number is NOT EQUAL to 198 +throw_unless(39, number == 198); // the error will be triggered only if the number is NOT EQUAL to 198 -throw(36); ;; the error will be triggered anyway +throw(36); // the error will be triggered anyway ``` [Standard tvm exception codes](/learn/tvm-instructions/tvm-exit-codes.md) @@ -804,7 +803,7 @@ forall X -> (tuple) to_tuple (X x) asm "NOP"; () main () { tuple t = to_tuple([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); tuple reversed_t = reverse_tuple(t); - ~dump(reversed_t); ;; [10 9 8 7 6 5 4 3 2 1] + ~dump(reversed_t); // [10 9 8 7 6 5 4 3 2 1] } ``` @@ -839,11 +838,11 @@ int tlen (tuple t) asm "TLEN"; numbers~tpush(999); numbers~tpush(54); - ~dump(numbers); ;; [19 999 54] + ~dump(numbers); // [19 999 54] numbers~remove_item(1); - ~dump(numbers); ;; [19 54] + ~dump(numbers); // [19 54] } ``` @@ -861,12 +860,12 @@ int are_slices_equal_2? (slice a, slice b) asm "SDEQ"; () main () { slice a = "Some text"; slice b = "Some text"; - ~dump(are_slices_equal_1?(a, b)); ;; -1 = true + ~dump(are_slices_equal_1?(a, b)); // -1 = true a = "Text"; - ;; We use literal `a` to get valid address inside slice from string containing address + // We use literal `a` to get valid address inside slice from string containing address b = "EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"a; - ~dump(are_slices_equal_2?(a, b)); ;; 0 = false + ~dump(are_slices_equal_2?(a, b)); // 0 = false } ``` @@ -893,7 +892,7 @@ int are_cells_equal? (cell a, cell b) { .store_uint(123, 16) .end_cell(); - ~dump(are_cells_equal?(a, b)); ;; -1 = true + ~dump(are_cells_equal?(a, b)); // -1 = true } ``` @@ -924,10 +923,10 @@ int are_cells_equal? (cell a, cell b) { } (int) are_tuples_equal? (tuple t1, tuple t2) { - int equal? = -1; ;; initial value to true + int equal? = -1; // initial value to true if (t1.tuple_length() != t2.tuple_length()) { - ;; if tuples are differ in length they cannot be equal + // if tuples are differ in length they cannot be equal return 0; } @@ -938,7 +937,7 @@ int are_cells_equal? (cell a, cell b) { var v2 = t2~tpop(); if (is_null(t1) & is_null(t2)) { - ;; nulls are always equal + // nulls are always equal } elseif (is_int(v1) & is_int(v2)) { if (cast_to_int(v1) != cast_to_int(v2)) { @@ -956,7 +955,7 @@ int are_cells_equal? (cell a, cell b) { } } elseif (is_tuple(v1) & is_tuple(v2)) { - ;; recursively determine nested tuples + // recursively determine nested tuples if (~ are_tuples_equal?(cast_to_tuple(v1), cast_to_tuple(v2))) { equal? = 0; } @@ -975,7 +974,7 @@ int are_cells_equal? (cell a, cell b) { tuple t1 = cast_to_tuple([[2, 6], [1, [3, [3, 5]]], 3]); tuple t2 = cast_to_tuple([[2, 6], [1, [3, [3, 5]]], 3]); - ~dump(are_tuples_equal?(t1, t2)); ;; -1 + ~dump(are_tuples_equal?(t1, t2)); // -1 } ``` @@ -993,19 +992,19 @@ Creates an internal address for the corresponding MsgAddressInt TLB. ```func (slice) generate_internal_address (int workchain_id, cell state_init) { - ;; addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; + // addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; return begin_cell() - .store_uint(2, 2) ;; addr_std$10 - .store_uint(0, 1) ;; anycast nothing - .store_int(workchain_id, 8) ;; workchain_id: -1 + .store_uint(2, 2) // addr_std$10 + .store_uint(0, 1) // anycast nothing + .store_int(workchain_id, 8) // workchain_id: -1 .store_uint(cell_hash(state_init), 256) .end_cell().begin_parse(); } () main () { slice deploy_address = generate_internal_address(workchain(), state_init); - ;; then we can deploy new contract + // then we can deploy new contract } ``` @@ -1025,12 +1024,12 @@ We use the TL-B scheme from [block.tlb](https://github.com/ton-blockchain/ton/bl (int) ubitsize (int a) asm "UBITSIZE"; slice generate_external_address (int address) { - ;; addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt; + // addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt; int address_length = ubitsize(address); return begin_cell() - .store_uint(1, 2) ;; addr_extern$01 + .store_uint(1, 2) // addr_extern$01 .store_uint(address_length, 9) .store_uint(address, address_length) .end_cell().begin_parse(); @@ -1079,14 +1078,14 @@ The usual way for us to send tons with a comment is actually a simple message. T ```func cell msg = begin_cell() - .store_uint(0x18, 6) ;; flags - .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; destination address - .store_coins(100) ;; amount of nanoTons to send - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - .store_uint(0, 32) ;; zero opcode - means simple transfer message with comment - .store_slice("Hello from FunC!") ;; comment + .store_uint(0x18, 6) // flags + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) // destination address + .store_coins(100) // amount of nanoTons to send + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) + .store_uint(0, 32) // zero opcode - means simple transfer message with comment + .store_slice("Hello from FunC!") // comment .end_cell(); -send_raw_message(msg, 3); ;; mode 3 - pay fees separately, ignore errors +send_raw_message(msg, 3); // mode 3 - pay fees separately, ignore errors ``` > 💡 Useful links @@ -1099,20 +1098,20 @@ The contract example below is useful to us if we need to perform any actions bet ```func () recv_internal (slice in_msg_body) { - {- + /* This is a simple example of a proxy-contract. It will expect in_msg_body to contain message mode, body and destination address to be sent to. - -} + */ - int mode = in_msg_body~load_uint(8); ;; first byte will contain msg mode - slice addr = in_msg_body~load_msg_addr(); ;; then we parse the destination address - slice body = in_msg_body; ;; everything that is left in in_msg_body will be our new message's body + int mode = in_msg_body~load_uint(8); // first byte will contain msg mode + slice addr = in_msg_body~load_msg_addr(); // then we parse the destination address + slice body = in_msg_body; // everything that is left in in_msg_body will be our new message's body cell msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) - .store_coins(100) ;; just for example - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_coins(100) // just for example + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) .store_slice(body) .end_cell(); send_raw_message(msg, mode); @@ -1131,14 +1130,14 @@ If we need to send the entire balance of the smart contract, then, in this case, ```func cell msg = begin_cell() - .store_uint(0x18, 6) ;; flags - .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; destination address - .store_coins(0) ;; we don't care about this value right now - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - .store_uint(0, 32) ;; zero opcode - means simple transfer message with comment - .store_slice("Hello from FunC!") ;; comment + .store_uint(0x18, 6) // flags + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) // destination address + .store_coins(0) // we don't care about this value right now + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) + .store_uint(0, 32) // zero opcode - means simple transfer message with comment + .store_slice("Hello from FunC!") // comment .end_cell(); -send_raw_message(msg, 128); ;; mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract +send_raw_message(msg, 128); // mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract ``` > 💡 Useful links @@ -1152,14 +1151,14 @@ send_raw_message(msg, 128); ;; mode = 128 is used for messages that are to carry As we know, only 127 characters can fit into a single `cell` (<1023 bits). In case we need more - we need to organize a snake cells. ```func -{- +/* If we want to send a message with really long comment, we should split the comment to several slices. Each slice should have <1023 bits of data (127 chars). Each slice should have a reference to the next one, forming a snake-like structure. --} +*/ cell body = begin_cell() - .store_uint(0, 32) ;; zero opcode - simple message with comment + .store_uint(0, 32) // zero opcode - simple message with comment .store_slice("long long long message...") .store_ref(begin_cell() .store_slice(" you can store string of almost any length here.") @@ -1170,15 +1169,15 @@ cell body = begin_cell() .end_cell(); cell msg = begin_cell() - .store_uint(0x18, 6) ;; flags - ;; We use literal `a` to get valid address inside slice from string containing address - .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; destination address - .store_coins(100) ;; amount of nanoTons to send - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) ;; default message headers (see sending messages page) - .store_uint(1, 1) ;; we want to store body as a ref + .store_uint(0x18, 6) // flags + // We use literal `a` to get valid address inside slice from string containing address + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) // destination address + .store_coins(100) // amount of nanoTons to send + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) // default message headers (see sending messages page) + .store_uint(1, 1) // we want to store body as a ref .store_ref(body) .end_cell(); -send_raw_message(msg, 3); ;; mode 3 - pay fees separately, ignore errors +send_raw_message(msg, 3); // mode 3 - pay fees separately, ignore errors ``` > 💡 Useful links @@ -1192,8 +1191,8 @@ If we are not interested in `refs` inside the `slice`, then we can get a separat ```func slice s = begin_cell() .store_slice("Some data bits...") - .store_ref(begin_cell().end_cell()) ;; some references - .store_ref(begin_cell().end_cell()) ;; some references + .store_ref(begin_cell().end_cell()) // some references + .store_ref(begin_cell().end_cell()) // some references .end_cell().begin_parse(); slice s_only_data = s.preload_bits(s.slice_bits()); @@ -1213,9 +1212,9 @@ Modifying methods allow data to be modified within the same variable. This can b ```func (slice, (int)) load_digit (slice s) { - int x = s~load_uint(8); ;; load 8 bits (one char) from slice - x -= 48; ;; char '0' has code of 48, so we substract it to get the digit as a number - return (s, (x)); ;; return our modified slice and loaded digit + int x = s~load_uint(8); // load 8 bits (one char) from slice + x -= 48; // char '0' has code of 48, so we substract it to get the digit as a number + return (s, (x)); // return our modified slice and loaded digit } () main () { @@ -1223,7 +1222,7 @@ Modifying methods allow data to be modified within the same variable. This can b int c1 = s~load_digit(); int c2 = s~load_digit(); int c3 = s~load_digit(); - ;; here s is equal to "", and c1 = 2, c2 = 5, c3 = 8 + // here s is equal to "", and c1 = 2, c2 = 5, c3 = 8 } ``` @@ -1234,7 +1233,7 @@ Modifying methods allow data to be modified within the same variable. This can b ### How to raise number to the power of n ```func -;; Unoptimized variant +// Unoptimized variant int pow (int a, int n) { int i = 0; int value = a; @@ -1245,7 +1244,7 @@ int pow (int a, int n) { return a; } -;; Optimized variant +// Optimized variant (int) binpow (int n, int e) { if (e == 0) { return 1; @@ -1263,7 +1262,7 @@ int pow (int a, int n) { () main () { int num = binpow(2, 3); - ~dump(num); ;; 8 + ~dump(num); // 8 } ``` @@ -1275,7 +1274,7 @@ int number = 0; while (~ string_number.slice_empty?()) { int char = string_number~load_uint(8); - number = (number * 10) + (char - 48); ;; we use ASCII table + number = (number * 10) + (char - 48); // we use ASCII table } ~dump(number); @@ -1310,10 +1309,10 @@ d~udict_set(256, 1, "value 1"); d~udict_set(256, 5, "value 2"); d~udict_set(256, 12, "value 3"); -;; iterate keys from small to big +// iterate keys from small to big (int key, slice val, int flag) = d.udict_get_min?(256); while (flag) { - ;; do something with pair key->val + // do something with pair key->val (key, val, flag) = d.udict_get_next?(256, key); } @@ -1341,7 +1340,7 @@ names~udict_set(256, 25, "Bob"); names~udict_delete?(256, 27); (slice val, int key) = names.udict_get?(256, 27); -~dump(val); ;; null() -> means that key was not found in a dictionary +~dump(val); // null() -> means that key was not found in a dictionary ``` ### How to iterate cell tree recursively @@ -1354,7 +1353,7 @@ forall X -> (tuple, ()) push_back (tuple tail, X head) asm "CONS"; forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; () main () { - ;; just some cell for example + // just some cell for example cell c = begin_cell() .store_uint(1, 16) .store_ref(begin_cell() @@ -1371,18 +1370,18 @@ forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; .end_cell()) .end_cell(); - ;; creating tuple with no data, which plays the role of stack + // creating tuple with no data, which plays the role of stack tuple stack = null(); - ;; bring the main cell into the stack to process it in the loop + // bring the main cell into the stack to process it in the loop stack~push_back(c); - ;; do it until stack is not null + // do it until stack is not null while (~ stack.is_null()) { - ;; get the cell from the stack and convert it to a slice to be able to process it + // get the cell from the stack and convert it to a slice to be able to process it slice s = stack~pop_back().begin_parse(); - ;; do something with s data + // do something with s data - ;; if the current slice has any refs, add them to stack + // if the current slice has any refs, add them to stack repeat (s.slice_refs()) { stack~push_back(s~load_ref()); } @@ -1408,18 +1407,18 @@ forall X -> (tuple, ()) push_back (tuple tail, X head) asm "CONS"; forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; () main () { - ;; some example list + // some example list tuple l = null(); l~push_back(1); l~push_back(2); l~push_back(3); - ;; iterating through elements - ;; note that this iteration is in reversed order + // iterating through elements + // note that this iteration is in reversed order while (~ l.is_null()) { var x = l~pop_back(); - ;; do something with x + // do something with x } } ``` @@ -1433,28 +1432,28 @@ forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; ### How to send a deploy message (with stateInit only, with stateInit and body) ```func -() deploy_with_stateinit(cell message_header, cell state_init) impure { +() deploy_with_stateinit(cell message_header, cell state_init) { var msg = begin_cell() .store_slice(begin_parse(msg_header)) - .store_uint(2 + 1, 2) ;; init:(Maybe (Either StateInit ^StateInit)) - .store_uint(0, 1) ;; body:(Either X ^X) + .store_uint(2 + 1, 2) // init:(Maybe (Either StateInit ^StateInit)) + .store_uint(0, 1) // body:(Either X ^X) .store_ref(state_init) .end_cell(); - ;; mode 64 - carry the remaining value in the new message + // mode 64 - carry the remaining value in the new message send_raw_message(msg, 64); } -() deploy_with_stateinit_body(cell message_header, cell state_init, cell body) impure { +() deploy_with_stateinit_body(cell message_header, cell state_init, cell body) { var msg = begin_cell() .store_slice(begin_parse(msg_header)) - .store_uint(2 + 1, 2) ;; init:(Maybe (Either StateInit ^StateInit)) - .store_uint(1, 1) ;; body:(Either X ^X) + .store_uint(2 + 1, 2) // init:(Maybe (Either StateInit ^StateInit)) + .store_uint(1, 1) // body:(Either X ^X) .store_ref(state_init) .store_ref(body) .end_cell(); - ;; mode 64 - carry the remaining value in the new message + // mode 64 - carry the remaining value in the new message send_raw_message(msg, 64); } ``` @@ -1464,11 +1463,11 @@ forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; ```func () build_stateinit(cell init_code, cell init_data) { var state_init = begin_cell() - .store_uint(0, 1) ;; split_depth:(Maybe (## 5)) - .store_uint(0, 1) ;; special:(Maybe TickTock) - .store_uint(1, 1) ;; (Maybe ^Cell) - .store_uint(1, 1) ;; (Maybe ^Cell) - .store_uint(0, 1) ;; (HashmapE 256 SimpleLib) + .store_uint(0, 1) // split_depth:(Maybe (## 5)) + .store_uint(0, 1) // special:(Maybe TickTock) + .store_uint(1, 1) // (Maybe ^Cell) + .store_uint(1, 1) // (Maybe ^Cell) + .store_uint(0, 1) // (HashmapE 256 SimpleLib) .store_ref(init_code) .store_ref(init_data) .end_cell(); @@ -1480,10 +1479,10 @@ forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; ```func () calc_address(cell state_init) { var future_address = begin_cell() - .store_uint(2, 2) ;; addr_std$10 - .store_uint(0, 1) ;; anycast:(Maybe Anycast) - .store_uint(0, 8) ;; workchain_id:int8 - .store_uint(cell_hash(state_init), 256) ;; address:bits256 + .store_uint(2, 2) // addr_std$10 + .store_uint(0, 1) // anycast:(Maybe Anycast) + .store_uint(0, 8) // workchain_id:int8 + .store_uint(cell_hash(state_init), 256) // address:bits256 .end_cell(); } ``` diff --git a/docs/develop/func/functions.md b/docs/develop/func/functions.md index d269897e3a..13847c92d5 100644 --- a/docs/develop/func/functions.md +++ b/docs/develop/func/functions.md @@ -34,7 +34,7 @@ Function name can be any [identifier](/develop/func/literals_identifiers#identif For example, `udict_add_builder?`, `dict_set` and `~dict_set` are valid and different function names. (They are defined in [stdlib.fc](/develop/func/stdlib).) #### Special function names -FunC (actually Fift assembler) has several reserved function names with predefined [ids](/develop/func/functions#method_id). +FunC (actually Fift assembler) has several reserved function names with predefined ids. - `main` and `recv_internal` have id = 0 - `recv_external` has id = -1 - `run_ticktock` has id = -2 @@ -200,25 +200,16 @@ In summary, when a function with the name `foo` is called as a non-modifying or ### Specifiers -There are three types of specifiers: `impure`, `inline`/`inline_ref`, and `method_id`. One, several, or none of them can be put in a function declaration but currently they must be presented in the right order. For example, it is not allowed to put `impure` after `inline`. -#### Impure specifier -`impure` specifier means that the function can have some side effects which can't be ignored. For example, we should put `impure` specifier if the function can modify contract storage, send messages, or throw an exception when some data is invalid and the function is intended to validate this data. +There are several specifiers that can be placed at function declaration. -If `impure` is not specified and the result of the function call is not used, then the FunC compiler may and will delete this function call. +#### `inline` specifier +If a function is `inline`, its code is substituted into every place where the function is called. +It goes without saying that recursive calls to inlined functions are not possible. -For example, in the [stdlib.fc](/develop/func/stdlib) function -```func -int random() impure asm "RANDU256"; -``` -is defined. `impure` is used because `RANDU256` changes the internal state of the random number generator. - -#### Inline specifier -If a function has `inline` specifier, its code is actually substituted in every place where the function is called. It goes without saying that recursive calls to inlined functions are not possible. - -For example, you can using `inline` like this way in this example: [ICO-Minter.fc](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/ft/jetton-minter-ICO.fc#L16) +Here is an example from [ICO-Minter.fc](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/ft/jetton-minter-ICO.fc#L16): ```func -() save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) impure inline { +() save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) inline { set_data(begin_cell() .store_coins(total_supply) .store_slice(admin_address) @@ -229,20 +220,67 @@ For example, you can using `inline` like this way in this example: [ICO-Minter.f } ``` +#### `inline_ref` specifier +The code of `inline_ref` function is put into a separate cell, and calling a function is done via `CALLREF` TVM command. +So it's similar to `inline`, but because a cell can be reused in several places without duplicating it, +it's almost always more efficient in terms of code size to use `inline_ref` specifier instead of `inline` +unless the function is called exactly once. +Recursive calls of `inline_ref`'ed functions are still impossible because there are no cyclic references in the TVM cells. + +#### `pure` specifier +:::caution +Before FunC v0.5.0, there was an `impure` specifier. +Now, all functions are impure by default, but many standard functions are `pure`. +::: +When a result of a `pure` function is not used, FunC will delete this function call. For example, +```func +int main() { + cell c = begin_cell().end_cell(); + return 0; +} +``` +will be just compiled to `0 PUSHINT`, since `c` is not used anywhere, and two calls are just dropped off. -#### Inline_ref specifier -The code of a function with the `inline_ref` specifier is put into a separate cell, and every time when the function is called, a `CALLREF` command is executed by TVM. So it's similar to `inline`, but because a cell can be reused in several places without duplicating it, it is almost always more efficient in terms of code size to use `inline_ref` specifier instead of `inline` unless the function is called exactly once. Recursive calls of `inline_ref`'ed functions are still impossible because there are no cyclic references in the TVM cells. -#### method_id -Every function in TVM program has an internal integer id by which it can be called. Ordinary functions are usually numbered by subsequent integers starting from 1, but get-methods of the contract are numbered by crc16 hashes of their names. `method_id()` specifier allows to set the id of a function to specified value, and `method_id` uses the default value `(crc16() & 0xffff) | 0x10000`. If a function has `method_id` specifier, then it can be called in lite-client or ton-explorer as a get-method by its name. +Impure functions, by their nature, have side effects (change TVM state or globals). +For example, `set_data()` changes the `c4` register, and is therefore impure. +Or `random()` changes the internal state of number generator. +Throwing functions are also impure. + +All functions are impure by default, so FunC will never strip calls to your functions unless you manually declare them as `pure`. + +### GET methods +:::caution +Before FunC v0.5.0, there was a `method_id` specifier. +Now it's deprecated as obscure, use `get` as explained below. +::: +If a function is marked `get`, it becomes a "get-method" and can be called in lite-client / ton-explorer / func-js by its name. -For example, ```func -(int, int) get_n_k() method_id { +get int balance() { + var [balance, _] = calculate_balance(); + return balance; +} + +get (int, int) primary_state() { (_, int n, int k, _, _, _, _) = unpack_state(); return (n, k); } ``` -is a get-method of multisig contract. + +Get methods are regular functions, they may also accept any parameters. +They are also impure (to be able to call custom, impure by default, functions), +but an attempt to change state (save data, for instance) won't do anything. + +**How does calling by name work?** Actually, function names are not stored in bytecode. +Instead, a hash = `(crc16() & 0xffff) | 0x10000` is saved. When calling by name, a client calculates the same hash, actually. +This hash is called **method_id** in TL and other low-level articles. + +:::danger +Don't confuse with Solidity "getters". In TON, one contract can't call a get method of another. +Contracts are asynchronous, they can only send messages to each other. +GET methods are what you expose not to other contracts, but to front-end of your dapp, for example. +Moreover, they are executed offchain. +::: ### Polymorphism with forall Before any function declaration or definition, there can be `forall` type variables declarator. It has the following syntax: @@ -287,10 +325,6 @@ The list of assembler commands can be found here: [TVM instructions](/learn/tvm- ### Rearranging stack entries In some cases, we want to pass arguments to the assembler function in a different order than the assembler command requires, or/and take the result in a different stack entry order than the command returns. We could manually rearrange the stack by adding corresponding stack primitives, but FunC can do it automatically. -:::info -Note, that in case of manual rearranging, arguments will be computed in the rearranged order. To overwrite this behavior use `#pragma compute-asm-ltr`: [compute-asm-ltr](compiler_directives#pragma-compute-asm-ltr) -::: - For example, suppose that the assembler command STUXQ takes an integer, builder, and integer; then it returns the builder, along with the integer flag, indicating the success or failure of the operation. We may define the function: ```func diff --git a/docs/develop/func/migration-guide.md b/docs/develop/func/migration-guide.md new file mode 100644 index 0000000000..f3cc1f8ec3 --- /dev/null +++ b/docs/develop/func/migration-guide.md @@ -0,0 +1,11 @@ +# FunC migration guide + +### From v0.4.x to v0.5.0 + +1. Download a new version of the compiler. If you use blueprint or func-js directly, just update a package to the latest version. +2. Download new [stdlib.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc) and replace your current (in the future, stdlib will be available out of the box, you won't need to download and store it in your project). +3. Update your IDE plugin (available for JetBrains and VS Code). +4. Choose "FunC language level" to "v0.5.x" in plugin settings. +5. Prefer to use `// traditional` `/* comments */` instead of old Lisp-style. IDE will suggest you to replace existing comments. +6. Don't use `impure`, since all functions are impure by default. IDE will suggest you to remove existing specifiers. +7. Don't use `method_id`, use `get` keyword on the left: `get int seqno() { ... }`. IDE will suggest you to replace existing specifiers. diff --git a/docs/develop/func/overview.mdx b/docs/develop/func/overview.mdx index c84969f2c7..11bae9176d 100644 --- a/docs/develop/func/overview.mdx +++ b/docs/develop/func/overview.mdx @@ -8,9 +8,9 @@ FunC is a domain-specific, C-like, statically typed language. Here is a simple example method for sending money written in FunC: ```func -() send_money(slice address, int amount) impure inline { +() send_money(slice address, int amount) inline { var msg = begin_cell() - .store_uint(0x10, 6) ;; nobounce + .store_uint(0x10, 6) // nobounce .store_slice(address) .store_coins(amount) .end_cell(); @@ -58,7 +58,7 @@ Module 4 completely covers FunC language and smart contracts development. ## Tutorials :::tip starter tip -The best place to start to develop using FunC: [INTRODUCTION](/develop/smart-contracts/) +The best place to start developing with FunC: [INTRODUCTION](/develop/smart-contracts/) ::: Other materials gracefully provided by the experts from the community: @@ -70,14 +70,14 @@ Other materials gracefully provided by the experts from the community: - [🚩 Challenge 4: Lottery/Raffle](https://github.com/romanovichim/TONQuest4) - [🚩 Challenge 5: Create UI to interact with the contract in 5 minutes](https://github.com/romanovichim/TONQuest5) - [🚩 Challenge 6: Analyzing NFT sales on the Getgems marketplace](https://github.com/romanovichim/TONQuest6) -* [Func & Blueprint](https://www.youtube.com/watch?v=7omBDfSqGfA&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) by **@MarcoDaTr0p0je** +* [FunC & Blueprint](https://www.youtube.com/watch?v=7omBDfSqGfA&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) by **@MarcoDaTr0p0je** * [Learn FunC in Y Minutes](https://learnxinyminutes.com/docs/func/) by **@romanovichim** * [TON Hello World: Step-by-step guide for writing your first smart contract](https://ton-community.github.io/tutorials/02-contract/) -* [TON Hello World: Step by step guide for testing your first smart contract](https://ton-community.github.io/tutorials/04-testing/) +* [TON Hello World: Step-by-step guide for testing your first smart contract](https://ton-community.github.io/tutorials/04-testing/) * [10 FunC Lessons](https://github.com/romanovichim/TonFunClessons_Eng) by **@romanovichim**, using blueprint * [10 FunC lessons (RU)](https://github.com/romanovichim/TonFunClessons_ru) by **@romanovichim**, using blueprint -* [FunC Quiz](https://t.me/toncontests/60) by **Vadim**—Good for selfcheck. It will take 10–15 minutes. The questions are mainly about FunС with a few general questions about TON -* [FunC Quiz (RU)](https://t.me/toncontests/58?comment=14888) by **Vadim**—FunC Quiz in Russian +* [FunC Quiz](https://t.me/toncontests/60) by **Vadim** — Good for selfcheck. It will take 10–15 minutes. The questions are mainly about FunС with a few general questions about TON +* [FunC Quiz (RU)](https://t.me/toncontests/58?comment=14888) by **Vadim** — FunC Quiz in Russian ## Contests @@ -102,6 +102,6 @@ Standard basic smart contracts like wallets, electors (manages validation on TON * [Smart contract examples](/develop/smart-contracts/examples) ## Changelog -[History of funC updates](/develop/func/changelog). +[History of FunC updates](/develop/func/changelog). diff --git a/docs/develop/func/statements.md b/docs/develop/func/statements.md index 92dd29bf69..d8890c4ed9 100644 --- a/docs/develop/func/statements.md +++ b/docs/develop/func/statements.md @@ -2,7 +2,7 @@ This section briefly discusses FunC statements, constituting the code of ordinary function bodies. ## Expression statements -The most common type of a statement is the expression statement. It's an expression followed by `;`. Expression's description would be quite complicated, so only a sketch is presented here. As a rule all sub-expressions are computed left to right with one exception of [asm stack rearrangement](functions#rearranging-stack-entries) which may define order manually. +The most common type of a statement is the expression statement. It's an expression followed by `;`. Expression's description would be quite complicated, so only a sketch is presented here. As a rule, all sub-expressions are computed left to right. ### Variable declaration It is not possible to declare a local variable without defining its initial value. @@ -36,10 +36,10 @@ int x = 0; int i = 0; while (i < 10) { (int, int) x = (i, i + 1); - ;; here x is a variable of type (int, int) + // here x is a variable of type (int, int) i += 1; } -;; here x is a (different) variable of type int +// here x is a (different) variable of type int ``` But as mentioned in the global variables [section](/develop/func/global_variables.md), a global variable cannot be redeclared. @@ -56,7 +56,7 @@ Underscore `_` is used when a value is not needed. For example, suppose a functi ### Function application A call of a function looks like as such in a conventional language. The arguments of the function call are listed after the function name, separated by commas. ```func -;; suppose foo has type (int, int, int) -> int +// suppose foo has type (int, int, int) -> int int x = foo(1, 2, 3); ``` @@ -72,12 +72,12 @@ int x = foo(a, b, c); Also Haskell-style calls are possible, but not always (to be fixed later): ```func -;; suppose foo has type int -> int -> int -> int -;; i.e. it's carried +// suppose foo has type int -> int -> int -> int +// i.e. it's carried (int a, int b, int c) = (1, 2, 3); -int x = foo a b c; ;; ok -;; int y = foo 1 2 3; wouldn't compile -int y = foo (1) (2) (3); ;; ok +int x = foo a b c; // ok +// int y = foo 1 2 3; wouldn't compile +int y = foo (1) (2) (3); // ok ``` ### Lambda expressions Lambda expressions are not supported yet. @@ -152,6 +152,14 @@ In summary, when a function with the name `foo` is called as a non-modifying or ### Operators Note that currently all of the unary and binary operators are integer operators. Logical operators are represented as bitwise integer operators (cf. [absence of boolean type](/develop/func/types#absence-of-boolean-type)). + +:::info Mind about spaces +- `x + y` is ok +- `x+y` is not ok (it's a single identifier) +- `~ x` is ok (expression "not x") +- `~x` is a modifier method +::: + #### Unary operators There are two unary operators: - `~` is bitwise not (priority 75) @@ -171,13 +179,10 @@ With priority 30 (left-associative): - `~%` is integer reduction by modulo (round) - `^%` is integer reduction by modulo (ceil) - `/%` returns the quotient and the remainder -- `&` is bitwise AND With priority 20 (left-associative): - `+` is integer addition - `-` is integer subtraction -- `|` is bitwise OR -- `^` is bitwise XOR With priority 17 (left-associative): - `<<` is bitwise left shift @@ -194,9 +199,15 @@ With priority 15 (left-associative): - `>=` is integer comparison - `<=>` is integer comparison (returns -1, 0 or 1) -They also should be separated from the argument: -- `x + y` is ok -- `x+y` is not ok (it's a single identifier) +With priority 14 (left-associative): +- `&` is bitwise AND +- `|` is bitwise OR +- `^` is bitwise XOR + +:::caution `& | ^` priority changed in FunC v0.5.0 +Before v0.5.0, they had higher priority, leading to errors hard to find out. +Now they are more intuitive: `if (op == 2 & val)` works as expected. +::: #### Conditional operator It has the usual syntax. @@ -223,21 +234,21 @@ int x = 1; repeat(10) { x *= 2; } -;; x = 1024 +// x = 1024 ``` ```func int x = 1, y = 10; repeat(y + 6) { x *= 2; } -;; x = 65536 +// x = 65536 ``` ```func int x = 1; repeat(-1) { x *= 2; } -;; x = 1 +// x = 1 ``` If the number of times is less than `-2^31` or greater than `2^31 - 1`, range check exception is thrown. ### While loop @@ -247,7 +258,7 @@ int x = 2; while (x < 100) { x = x * x; } -;; x = 256 +// x = 256 ``` Note that the truth value of condition `x < 100` is of type `int` (cf. [absence of boolean type](/develop/func/types#absence-of-boolean-type)). @@ -258,24 +269,24 @@ int x = 0; do { x += 3; } until (x % 17 == 0); -;; x = 51 +// x = 51 ``` ## If statements Examples: ```func -;; usual if +// usual if if (flag) { do_something(); } ``` ```func -;; equivalent to if (~ flag) +// equivalent to if (~ flag) ifnot (flag) { do_something(); } ``` ```func -;; usual if-else +// usual if-else if (flag) { do_something(); } @@ -284,7 +295,7 @@ else { } ``` ```func -;; Some specific features +// Some specific features if (flag1) { do_something1(); } else { @@ -298,13 +309,13 @@ if (flag1) ``` ## Try-Catch statements -*Available in func since v0.4.0* +*Available since FunC v0.4.0* Executes the code in `try` block. If it fails, completely rolls back changes made in `try` block and executes `catch` block instead; `catch` receives two arguments: the exception parameter of any type (`x`) and the error code (`n`, integer). Unlike many other languages in the FunC try-catch statement, the changes made in the try block, in particular the modification of local and global variables, all registers' changes (i.e. `c4` storage register, `c5` action/message register, `c7` context register and others) **are discarded** if there is an error in the try block and consequently all contract storage updates and message sending will be reverted. It is important to note that some TVM state parameters such as _codepage_ and gas counters will not be rolled back. This means, in particular, that all gas spent in the try block will be taken into account, and the effects of OPs that change the gas limit (`accept_message` and `set_gas_limit`) will be preserved. -Note that exception parameter can be of any type (possibly different in case of different exceptions) and thus funC can not predict it on compile time. That means that developer need to "help" compiler by casting exception parameter to some type (see Example 2 below): +Note that exception parameter can be of any type (possibly different in case of different exceptions) and thus FunC can not predict it on compile time. That means that developer need to "help" compiler by casting exception parameter to some type (see Example 2 below): Examples: ```func @@ -321,7 +332,7 @@ try { throw_arg(-1, 100); } catch (x, n) { x.cast_to_int(); - ;; x = -1, n = 100 + // x = -1, n = 100 return x + 1; } ``` @@ -332,7 +343,7 @@ try { throw(100); } catch (_, _) { } -;; x = 0 (not 1) +// x = 0 (not 1) ``` ## Block statements diff --git a/docs/develop/func/stdlib.mdx b/docs/develop/func/stdlib.mdx index 7433b55c75..713a0db977 100644 --- a/docs/develop/func/stdlib.mdx +++ b/docs/develop/func/stdlib.mdx @@ -9,12 +9,31 @@ toc_max_heading_level: 6 This section discusses the [stdlib.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc) library with standard functions used in FunC. ::: -Currently, the library is just a wrapper for the most common assembler of the TVM commands which are not built-in. Each TVM command description used in the library can be found in the [TVM documentation](/learn/tvm-instructions/tvm-overview) section. Some descriptions were borrowed for this document. - -Some functions are commented out in the file. It means that they have already become built-ins for optimization purposes. However, the type signature and semantics remain the same. +Currently, the library is just a wrapper for the most common assembler of the TVM commands. It also lists functions that are built-in for optimization purposes. Each TVM command description used in the library can be found in the [TVM documentation](/learn/tvm-instructions/tvm-overview) section. Some descriptions were borrowed for this document. Note that some less common commands are not presented in the stdlib. Someday they will also be added. +## Booleans + +- `true` is alias for `-1` +- `false` is alias for `0` + +## Throwing exceptions + +Exceptions can be thrown by conditional primitives `throw_if`, and `throw_unless`, and by unconditional `throw`. +The first argument is the error code; the second is the condition. +These primitives have parametrized versions `throw_arg_if`, `throw_arg_unless`, and `throw_arg`, where the first argument is the exception parameter of any type. + +```func +() throw(int excno) builtin; +() throw_if(int excno, int cond) builtin; +() throw_unless(int excno, int cond) builtin; + +forall X -> () throw_arg(X x, int excno) builtin; +forall X -> () throw_arg_if(X x, int excno, int cond) builtin; +forall X -> () throw_arg_unless(X x, int excno, int cond) builtin; +``` + ## Tuple manipulation primitives The names and the types are mostly self-explaining. See [polymorhism with forall](/develop/func/functions#polymorphism-with-forall) for more info on the polymorphic functions. @@ -28,7 +47,7 @@ Lists can be represented as nested 2-element tuples. Empty list is conventionall #### cons ```func -forall X -> tuple cons(X head, tuple tail) asm "CONS"; +forall X -> tuple cons(X head, tuple tail) pure asm "CONS"; ``` Adds an element to the beginning of a lisp-style list. @@ -36,7 +55,7 @@ Adds an element to the beginning of a lisp-style list. #### uncons ```func -forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; +forall X -> (X, tuple) uncons(tuple list) pure asm "UNCONS"; ``` Extracts the head and the tail of lisp-style list. @@ -44,23 +63,23 @@ Extracts the head and the tail of lisp-style list. #### list_next ```func -forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS"; +forall X -> (tuple, X) list_next(tuple list) pure asm( -> 1 0) "UNCONS"; ``` Extracts the head and tail of a lisp-style list. Can be used as a [(non-)modifying method](/develop/func/statements#methods-calls). ```func () foo(tuple xs) { - (_, int x) = xs.list_next(); ;; get the first element, `_` means do not use tail list - int y = xs~list_next(); ;; pop the first element - int z = xs~list_next(); ;; pop the second element + (_, int x) = xs.list_next(); // get the first element, `_` means do not use tail list + int y = xs~list_next(); // pop the first element + int z = xs~list_next(); // pop the second element } ``` #### car ```func -forall X -> X car(tuple list) asm "CAR"; +forall X -> X car(tuple list) pure asm "CAR"; ``` Returns the head of a lisp-style list. @@ -68,7 +87,7 @@ Returns the head of a lisp-style list. #### cdr ```func -tuple cdr(tuple list) asm "CDR"; +tuple cdr(tuple list) pure asm "CDR"; ``` Returns the tail of a lisp-style list. @@ -78,7 +97,7 @@ Returns the tail of a lisp-style list. #### empty_tuple ```func -tuple empty_tuple() asm "NIL"; +tuple empty_tuple() pure asm "NIL"; ``` Creates 0-element tuple. @@ -86,8 +105,8 @@ Creates 0-element tuple. #### tpush ```func -forall X -> tuple tpush(tuple t, X value) asm "TPUSH"; -forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH"; +forall X -> tuple tpush(tuple t, X value) pure asm "TPUSH"; +forall X -> (tuple, ()) ~tpush(tuple t, X value) pure asm "TPUSH"; ``` Appends the value `x` to the `Tuple t = (x1, ..., xn)` but only if the resulting `Tuple t' = (x1, ..., xn, x)` is no longer than 255 characters. Otherwise, a type check exception is thrown. @@ -95,7 +114,7 @@ Appends the value `x` to the `Tuple t = (x1, ..., xn)` but only if the resulting #### single ```func -forall X -> [X] single(X x) asm "SINGLE"; +forall X -> [X] single(X x) pure asm "SINGLE"; ``` Creates a singleton, i.e., a tuple of length one. @@ -103,7 +122,7 @@ Creates a singleton, i.e., a tuple of length one. #### unsingle ```func -forall X -> X unsingle([X] t) asm "UNSINGLE"; +forall X -> X unsingle([X] t) pure asm "UNSINGLE"; ``` Unpacks a singleton. @@ -111,7 +130,7 @@ Unpacks a singleton. #### pair ```func -forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR"; +forall X, Y -> [X, Y] pair(X x, Y y) pure asm "PAIR"; ``` Creates a pair. @@ -119,7 +138,7 @@ Creates a pair. #### unpair ```func -forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR"; +forall X, Y -> (X, Y) unpair([X, Y] t) pure asm "UNPAIR"; ``` Unpacks a pair. @@ -127,7 +146,7 @@ Unpacks a pair. #### triple ```func -forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE"; +forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) pure asm "TRIPLE"; ``` Creates a triple. @@ -135,7 +154,7 @@ Creates a triple. #### untriple ```func -forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE"; +forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) pure asm "UNTRIPLE"; ``` Unpacks a triple. @@ -143,7 +162,7 @@ Unpacks a triple. #### tuple4 ```func -forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE"; +forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) pure asm "4 TUPLE"; ``` Creates 4-element tuple. @@ -151,7 +170,7 @@ Creates 4-element tuple. #### untuple4 ```func -forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE"; +forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) pure asm "4 UNTUPLE"; ``` Unpacks 4-element tuple. @@ -159,7 +178,7 @@ Unpacks 4-element tuple. #### first ```func -forall X -> X first(tuple t) asm "FIRST"; +forall X -> X first(tuple t) pure asm "FIRST"; ``` Returns the first element of a tuple. @@ -167,7 +186,7 @@ Returns the first element of a tuple. #### second ```func -forall X -> X second(tuple t) asm "SECOND"; +forall X -> X second(tuple t) pure asm "SECOND"; ``` Returns the second element of a tuple. @@ -175,7 +194,7 @@ Returns the second element of a tuple. #### third ```func -forall X -> X third(tuple t) asm "THIRD"; +forall X -> X third(tuple t) pure asm "THIRD"; ``` Returns the third element of a tuple. @@ -183,15 +202,23 @@ Returns the third element of a tuple. #### fourth ```func -forall X -> X fourth(tuple t) asm "3 INDEX"; +forall X -> X fourth(tuple t) pure asm "3 INDEX"; ``` Returns the fourth element of a tuple. +#### at + +```func +forall X -> X at(tuple t, int index) pure builtin; +``` + +Returns the `index`-th element of a tuple. + #### pair_first ```func -forall X, Y -> X pair_first([X, Y] p) asm "FIRST"; +forall X, Y -> X pair_first([X, Y] p) pure asm "FIRST"; ``` Returns the first element of a pair. @@ -199,7 +226,7 @@ Returns the first element of a pair. #### pair_second ```func -forall X, Y -> Y pair_second([X, Y] p) asm "SECOND"; +forall X, Y -> Y pair_second([X, Y] p) pure asm "SECOND"; ``` Returns the second element of a pair. @@ -207,7 +234,7 @@ Returns the second element of a pair. #### triple_first ```func -forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST"; +forall X, Y, Z -> X triple_first([X, Y, Z] p) pure asm "FIRST"; ``` Returns the first element of a triple. @@ -215,7 +242,7 @@ Returns the first element of a triple. #### triple_second ```func -forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND"; +forall X, Y, Z -> Y triple_second([X, Y, Z] p) pure asm "SECOND"; ``` Returns the second element of a triple. @@ -223,7 +250,7 @@ Returns the second element of a triple. #### triple_third ```func -forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD"; +forall X, Y, Z -> Z triple_third([X, Y, Z] p) pure asm "THIRD"; ``` Returns the third element of a triple. @@ -237,7 +264,7 @@ Some useful information regarding smart contract invocation can be found in the #### now ```func -int now() asm "NOW"; +int now() pure asm "NOW"; ``` Returns the current Unix time as an Integer @@ -245,7 +272,7 @@ Returns the current Unix time as an Integer #### my_address ```func -slice my_address() asm "MYADDR"; +slice my_address() pure asm "MYADDR"; ``` Returns the internal address of the current smart contract as a Slice with `MsgAddressInt`. If necessary, it can be parsed further using primitives such as `parse_std_addr`. @@ -253,7 +280,7 @@ Returns the internal address of the current smart contract as a Slice with `MsgA #### get_balance ```func -[int, cell] get_balance() asm "BALANCE"; +[int, cell] get_balance() pure asm "BALANCE"; ``` Returns the remaining balance of the smart contract as `tuple` consisting of `int` (the remaining balance in nanotoncoins) and `cell` (a dictionary with 32-bit keys representing the balance of “extra currencies”). Note that RAW primitives such as `send_raw_message` do not update this field. @@ -261,7 +288,7 @@ Returns the remaining balance of the smart contract as `tuple` consisting of `in #### cur_lt ```func -int cur_lt() asm "LTIME"; +int cur_lt() pure asm "LTIME"; ``` Returns the logical time of the current transaction. @@ -269,7 +296,7 @@ Returns the logical time of the current transaction. #### block_lt ```func -int block_lt() asm "BLOCKLT"; +int block_lt() pure asm "BLOCKLT"; ``` Returns the starting logical time of the current @@ -278,7 +305,7 @@ block. #### config_param ```func -cell config_param(int x) asm "CONFIGOPTPARAM"; +cell config_param(int x) pure asm "CONFIGOPTPARAM"; ``` Returns the value of the global configuration parameter with integer index `i` as `cell` or `null` value. @@ -288,7 +315,7 @@ Returns the value of the global configuration parameter with integer index `i` a #### cell_hash ```func -int cell_hash(cell c) asm "HASHCU"; +int cell_hash(cell c) pure asm "HASHCU"; ``` Computes the representation hash of `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. @@ -296,7 +323,7 @@ Computes the representation hash of `cell c` and returns it as a 256-bit unsigne #### slice_hash ```func -int slice_hash(slice s) asm "HASHSU"; +int slice_hash(slice s) pure asm "HASHSU"; ``` Computes the hash of `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 `cell_hash`. @@ -304,7 +331,7 @@ Computes the hash of `slice s` and returns it as a 256-bit unsigned integer `x`. #### string_hash ```func -int string_hash(slice s) asm "SHA256U"; +int string_hash(slice s) pure asm "SHA256U"; ``` Computes sha256 of the data bits of `slice s`. If the bit length of `s` is not divisible by eight, it throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`. @@ -314,7 +341,7 @@ Computes sha256 of the data bits of `slice s`. If the bit length of `s` is not d #### check_signature ```func -int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU"; +int check_signature(int hash, slice signature, int public_key) pure asm "CHKSIGNU"; ``` Checks the Ed25519 `signature` of `hash` (a 256-bit unsigned integer, usually computed as the hash of some data) using `public_key` (also represented by a 256-bit unsigned integer). The signature must contain at least 512 data bits; only the first 512 bits are used. If the signature is valid, the result is `-1`; otherwise, it is `0`. 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, this data is hashed _twice_, the second hashing occurring inside `CHKSIGNS`. @@ -322,7 +349,7 @@ Checks the Ed25519 `signature` of `hash` (a 256-bit unsigned integer, usually co #### check_data_signature ```func -int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS"; +int check_data_signature(slice data, slice signature, int public_key) pure asm "CHKSIGNS"; ``` Checks whether `signature` is a valid Ed25519 signature of the data portion of `slice data` using `public_key`, similarly to `check_signature`. If the bit length of `data` is not divisible by eight, it throws a cell underflow exception. The verification of Ed25519 signatures is a standard one, with sha256 used to reduce `data` to the 256-bit number that is actually signed. @@ -334,7 +361,7 @@ The primitives below may be useful for computing storage fees for user-provided #### compute_data_size? ```func -(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +(int, int, int, int) compute_data_size?(cell c, int max_cells) pure asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; ``` 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 `max_cells`; otherwise, the computation is aborted before visiting the `(max_cells + 1)`-st cell and a zero flag is returned to indicate failure. If `c` is `null`, it returns `x = y = z = 0`. @@ -342,7 +369,7 @@ Returns `(x, y, z, -1)` or `(null, null, null, 0)`. Recursively computes the cou #### slice_compute_data_size? ```func -(int, int, int, int) slice_compute_data_size?(slice s, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +(int, int, int, int) slice_compute_data_size?(slice s, int max_cells) pure asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; ``` Similar to `compute_data_size?` but accepting `slice s` instead of `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`. @@ -350,7 +377,7 @@ Similar to `compute_data_size?` but accepting `slice s` instead of `cell`. The r #### compute_data_size ```func -(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE"; +(int, int, int) compute_data_size(cell c, int max_cells) asm "CDATASIZE"; ``` A non-quiet version of `compute_data_size?` that throws a cell overflow exception (8) on failure. @@ -358,7 +385,7 @@ A non-quiet version of `compute_data_size?` that throws a cell overflow exceptio #### slice_compute_data_size ```func -(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE"; +(int, int, int) slice_compute_data_size(slice s, int max_cells) asm "SDATASIZE"; ``` A non-quiet version of `slice_compute_data_size?` that throws a cell overflow exception (8) on failure. @@ -368,7 +395,7 @@ A non-quiet version of `slice_compute_data_size?` that throws a cell overflow ex #### get_data ```func -cell get_data() asm "c4 PUSH"; +cell get_data() pure asm "c4 PUSH"; ``` Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later. @@ -376,7 +403,7 @@ Returns the persistent contract storage cell. It can be parsed or modified with #### set_data ```func -() set_data(cell c) impure asm "c4 POP"; +() set_data(cell c) asm "c4 POP"; ``` Sets cell `c` as persistent contract data. You can update the persistent contract storage with this primitive. @@ -386,7 +413,7 @@ Sets cell `c` as persistent contract data. You can update the persistent contrac #### get_c3 ```func -cont get_c3() impure asm "c3 PUSH"; +cont get_c3() pure asm "c3 PUSH"; ``` 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`. @@ -394,7 +421,7 @@ Usually `c3` has a continuation initialized by the whole code of the contract. I #### set_c3 ```func -() set_c3(cont c) impure asm "c3 POP"; +() set_c3(cont c) asm "c3 POP"; ``` Updates the current value of `c3`. Usually, it is used for updating smart contract code in runtime. 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. @@ -402,7 +429,7 @@ Updates the current value of `c3`. Usually, it is used for updating smart contra #### bless ```func -cont bless(slice s) impure asm "BLESS"; +cont bless(slice s) pure asm "BLESS"; ``` Transforms `slice s` into a simple ordinary continuation `c` with `c.code = s`, and an empty stack, and savelist. @@ -412,7 +439,7 @@ Transforms `slice s` into a simple ordinary continuation `c` with `c.code = s`, #### accept_message ```func -() accept_message() impure asm "ACCEPT"; +() accept_message() asm "ACCEPT"; ``` Sets the current gas limit `gl` to its maximum allowed value `gm` and resets the gas credit `gc` to zero, decreasing the value of `gr` by `gc` in the process. In other words, the current smart contract agrees to buy some gas to finish the current transaction. This action is required to process external messages that carry no value (hence no gas). @@ -422,7 +449,7 @@ For more details check [accept_message effects](/develop/smart-contracts/guideli #### set_gas_limit ```func -() set_gas_limit(int limit) impure asm "SETGASLIMIT"; +() set_gas_limit(int limit) asm "SETGASLIMIT"; ``` Sets the current gas limit `gl` to the minimum of `limit` and `gm`, and resets the gas credit `gc` to zero. At that point, if the amount of consumed gas (including the present instruction) exceeds the resulting value of `gl`, an (unhandled) out of gas exception is thrown before setting new gas limits. Notice that `set_gas_limit` with an argument `limit ≥ 2^63 − 1` is equivalent to `accept_message`. @@ -432,7 +459,7 @@ For more details check [accept_message effects](/develop/smart-contracts/guideli #### commit ```func -() commit() impure asm "COMMIT"; +() commit() asm "COMMIT"; ``` 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 is thrown later. @@ -440,7 +467,7 @@ Commits the current state of registers `c4` (“persistent data”) and `c5` ( #### buy_gas ```func -() buy_gas(int gram) impure asm "BUYGAS"; +() buy_gas(int gram) asm "BUYGAS"; ``` :::caution @@ -454,7 +481,7 @@ Computes the amount of gas that can be bought for `gram` nanotoncoins and sets ` #### raw_reserve ```func -() raw_reserve(int amount, int mode) impure asm "RAWRESERVE"; +() raw_reserve(int amount, int mode) asm "RAWRESERVE"; ``` Creates an output action which would reserve exactly `amount` nanotoncoins (if `mode = 0`), at most `amount` nanotoncoins (if `mode = 2`), or all but `amount` nanotoncoins (if `mode = 1` or `mode = 3`) from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying `amount` nanotoncoins (or `b − amount` nanotoncoins, where `b` is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in `mode` means that the external action does not fail if the specified amount cannot be reserved; instead, all the remaining balance is reserved. Bit +8 in `mode` means `amount <- -amount` before performing any further actions. Bit +4 in `mode` means that `amount` is increased by the original balance of the current account (before the compute phase), including all extra currencies before performing any other checks and actions. Currently, `amount` must be a non-negative integer, and `mode` must be in the range `0..15`. @@ -462,7 +489,7 @@ Creates an output action which would reserve exactly `amount` nanotoncoins (if ` #### raw_reserve_extra ```func -() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX"; +() raw_reserve_extra(int amount, cell extra_amount, int mode) asm "RAWRESERVEX"; ``` Similar to `raw_reserve` but also accepts a dictionary `extra_amount` (represented by `cell` or `null`) with extra currencies. In this way, currencies other than Toncoin can be reserved. @@ -470,7 +497,7 @@ Similar to `raw_reserve` but also accepts a dictionary `extra_amount` (represent #### send_raw_message ```func -() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG"; +() send_raw_message(cell msg, int mode) asm "SENDRAWMSG"; ``` Sends a raw message contained in `msg`, which should contain a correctly serialized object Message X, with the only exception that the source address is allowed to have a dummy value `addr_none` (to be automatically replaced with the current smart contract address), and `ihr_fee`, `fwd_fee`, `created_lt` and `created_at` fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). The integer parameter `mode` contains the flags. @@ -513,7 +540,7 @@ For example, if you want to send a regular message and pay transfer fees separat #### set_code ```func -() set_code(cell new_code) impure asm "SETCODE"; +() set_code(cell new_code) asm "SETCODE"; ``` Creates an output action that would change this smart contract code to that given by cell `new_code`. Notice that this change will take effect only after the successful termination of the current run of the smart contract. (Cf. [set_c3](/develop/func/stdlib#set_c3.)) @@ -532,7 +559,7 @@ Keep in mind that random numbers generated by the functions below can be predict #### random ```func -int random() impure asm "RANDU256"; +int random() asm "RANDU256"; ``` Generates a new pseudo-random unsigned 256-bit integer `x`. The algorithm is as follows: if `r` is the old value of the random seed considered a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its `sha512(r)` is computed; the first 32 bytes of this hash are stored as the new value `r'` of the random seed, and the remaining 32 bytes are returned as the next random value `x`. @@ -540,7 +567,7 @@ Generates a new pseudo-random unsigned 256-bit integer `x`. The algorithm is as #### rand ```func -int rand(int range) impure asm "RAND"; +int rand(int range) asm "RAND"; ``` 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 @@ -549,7 +576,7 @@ computed. #### get_seed ```func -int get_seed() impure asm "RANDSEED"; +int get_seed() pure asm "RANDSEED"; ``` Returns the current random seed as an unsigned 256-bit integer. @@ -557,7 +584,7 @@ Returns the current random seed as an unsigned 256-bit integer. #### set_seed ```func -int set_seed(int seed) impure asm "SETRAND"; +int set_seed(int seed) asm "SETRAND"; ``` Sets a random seed to an unsigned 256-bit `seed`. @@ -565,7 +592,7 @@ Sets a random seed to an unsigned 256-bit `seed`. #### randomize ```func -() randomize(int x) impure asm "ADDRAND"; +() randomize(int x) asm "ADDRAND"; ``` Mixes an unsigned 256-bit integer `x` into a random seed `r` by setting the random seed to sha256 of the concatenation of two 32-byte strings: the first with a big-endian representation of the old seed `r`, and the second with a big-endian representation of `x`. @@ -573,7 +600,7 @@ Mixes an unsigned 256-bit integer `x` into a random seed `r` by setting the rand #### randomize_lt ```func -() randomize_lt() impure asm "LTIME" "ADDRAND"; +() randomize_lt() asm "LTIME" "ADDRAND"; ``` Equivalent to `randomize(cur_lt());`. @@ -582,7 +609,7 @@ Equivalent to `randomize(cur_lt());`. The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme. -```func +```tlb addr_none$00 = MsgAddressExt; addr_extern$01 len:(## 8) external_address:(bits len) @@ -620,7 +647,7 @@ A deserialized `MsgAddress` is represented by the tuple `t` as follows: #### load_msg_addr ```func -(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR"; +(slice, slice) load_msg_addr(slice s) pure asm( -> 1 0) "LDMSGADDR"; ``` 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. @@ -628,7 +655,7 @@ Loads from `slice s` the only prefix that is a valid `MsgAddress` and returns bo #### parse_addr ```func -tuple parse_addr(slice s) asm "PARSEMSGADDR"; +tuple parse_addr(slice s) pure asm "PARSEMSGADDR"; ``` Decomposes `slice s` containing a valid `MsgAddress` into `tuple t` with separate fields of this `MsgAddress`. If `s` is not a valid `MsgAddress`, a cell deserialization exception is thrown. @@ -636,7 +663,7 @@ Decomposes `slice s` containing a valid `MsgAddress` into `tuple t` with separat #### parse_std_addr ```func -(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR"; +(int, int) parse_std_addr(slice s) pure asm "REWRITESTDADDR"; ``` Parses slice `s` containing a valid `MsgAddressInt` (usually `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. @@ -644,7 +671,7 @@ Parses slice `s` containing a valid `MsgAddressInt` (usually `msg_addr_std`), ap #### parse_var_addr ```func -(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR"; +(int, slice) parse_var_addr(slice s) pure asm "REWRITEVARADDR"; ``` A variant of `parse_std_addr` that returns the (rewritten) address as a slice `s`, even if it is not exactly 256 bit long (represented by `msg_addr_var`). @@ -656,7 +683,7 @@ Debug primitives can be used for inspecting state of various variables while run #### ~dump ```func -forall X -> () ~dump(X value) impure asm "s0 DUMP"; +forall X -> (X, ()) ~dump(X x) builtin; ``` Outputs a value. Several values can be dumped as a tuple, e.g. `~dump([v1, v2, v3])`. @@ -664,7 +691,7 @@ Outputs a value. Several values can be dumped as a tuple, e.g. `~dump([v1, v2, v #### ~strdump ```func -() ~strdump(slice str) impure asm "STRDUMP"; +forall X -> (X, ()) ~strdump(X x) builtin; ``` Dumps a string. Slice parameter bit length must be divisible by 8. @@ -672,7 +699,7 @@ Dumps a string. Slice parameter bit length must be divisible by 8. #### dump_stack ```func -() dump_stack() impure asm "DUMPSTK"; +() dump_stack() asm "DUMPSTK"; ``` Dumps the stack (at most the top 255 values) and shows the total stack depth. @@ -688,7 +715,7 @@ Unless otherwise stated, loading and preloading primitives read data from a pref #### begin_parse ```func -slice begin_parse(cell c) asm "CTOS"; +slice begin_parse(cell c) pure asm "CTOS"; ``` Converts `cell` into `slice`. Notice that `c` must be either an ordinary cell or an exotic cell (see [TVM.pdf](https://ton.org/tvm.pdf), 3.1.2) which is automatically loaded to yield an ordinary cell `c'`converted into `slice` afterwards. @@ -696,7 +723,7 @@ Converts `cell` into `slice`. Notice that `c` must be either an ordinary cell or #### end_parse ```func -() end_parse(slice s) impure asm "ENDS"; +() end_parse(slice s) asm "ENDS"; ``` Checks if `s` is empty. If not, throws an exception. @@ -704,7 +731,7 @@ Checks if `s` is empty. If not, throws an exception. #### load_ref ```func -(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; +(slice, cell) load_ref(slice s) pure asm( -> 1 0) "LDREF"; ``` Loads the first reference from a slice. @@ -712,7 +739,7 @@ Loads the first reference from a slice. #### preload_ref ```func -cell preload_ref(slice s) asm "PLDREF"; +cell preload_ref(slice s) pure asm "PLDREF"; ``` Preloads the first reference from a slice. @@ -720,7 +747,7 @@ Preloads the first reference from a slice. #### load_int ```func -;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +(slice, int) load_int(slice s, int len) pure builtin; ``` Loads a signed `len`-bit integer from a slice. @@ -728,7 +755,7 @@ Loads a signed `len`-bit integer from a slice. #### load_uint ```func -;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; +(slice, int) load_uint(slice s, int len) pure builtin; ``` Loads an unsigned `len`-bit integer from a slice. @@ -736,7 +763,7 @@ Loads an unsigned `len`-bit integer from a slice. #### preload_int ```func -;; int preload_int(slice s, int len) asm "PLDIX"; +int preload_int(slice s, int len) pure builtin; ``` Preloads a signed `len`-bit integer from a slice. @@ -744,7 +771,7 @@ Preloads a signed `len`-bit integer from a slice. #### preload_uint ```func -;; int preload_uint(slice s, int len) asm "PLDUX"; +int preload_uint(slice s, int len) pure builtin; ``` Preloads an unsigned `len`-bit integer from a slice. @@ -752,7 +779,7 @@ Preloads an unsigned `len`-bit integer from a slice. #### load_bits ```func -;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; +(slice, slice) load_bits(slice s, int len) pure builtin; ``` Loads the first `0 ≤ len ≤ 1023` bits from slice `s` into a separate slice `s''`. @@ -760,7 +787,7 @@ Loads the first `0 ≤ len ≤ 1023` bits from slice `s` into a separate slice ` #### preload_bits ```func -;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; +slice preload_bits(slice s, int len) pure builtin; ``` Preloads the first `0 ≤ len ≤ 1023` bits from slice `s` into a separate slice `s''`. @@ -768,7 +795,7 @@ Preloads the first `0 ≤ len ≤ 1023` bits from slice `s` into a separate slic #### load_coins ```func -(slice, int) load_coins(slice s) asm( -> 1 0) "LDGRAMS"; +(slice, int) load_coins(slice s) pure asm( -> 1 0) "LDGRAMS"; ``` Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`). @@ -776,8 +803,8 @@ Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`). #### skip_bits ```func -slice skip_bits(slice s, int len) asm "SDSKIPFIRST"; -(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST"; +slice skip_bits(slice s, int len) pure asm "SDSKIPFIRST"; +(slice, ()) ~skip_bits(slice s, int len) pure asm "SDSKIPFIRST"; ``` Returns all but the first `0 ≤ len ≤ 1023` bits of `s`. @@ -785,7 +812,7 @@ Returns all but the first `0 ≤ len ≤ 1023` bits of `s`. #### first_bits ```func -slice first_bits(slice s, int len) asm "SDCUTFIRST"; +slice first_bits(slice s, int len) pure asm "SDCUTFIRST"; ``` Returns the first `0 ≤ len ≤ 1023` bits of `s`. @@ -793,8 +820,8 @@ Returns the first `0 ≤ len ≤ 1023` bits of `s`. #### skip_last_bits ```func -slice skip_last_bits(slice s, int len) asm "SDSKIPLAST"; -(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST"; +slice skip_last_bits(slice s, int len) pure asm "SDSKIPLAST"; +(slice, ()) ~skip_last_bits(slice s, int len) pure asm "SDSKIPLAST"; ``` Returns all but the last `0 ≤ len ≤ 1023` bits of `s`. @@ -802,7 +829,7 @@ Returns all but the last `0 ≤ len ≤ 1023` bits of `s`. #### slice_last ```func -slice slice_last(slice s, int len) asm "SDCUTLAST"; +slice slice_last(slice s, int len) pure asm "SDCUTLAST"; ``` Returns the last `0 ≤ len ≤ 1023` bits of `s`. @@ -810,7 +837,7 @@ Returns the last `0 ≤ len ≤ 1023` bits of `s`. #### load_dict ```func -(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT"; +(slice, cell) load_dict(slice s) pure asm( -> 1 0) "LDDICT"; ``` Loads a dictionary `D` from slice `s`. May be applied to dictionaries or to values of arbitrary `Maybe ^Y` types (returns `null` if `nothing` constructor is used). @@ -818,7 +845,7 @@ Loads a dictionary `D` from slice `s`. May be applied to dictionaries or to valu #### preload_dict ```func -cell preload_dict(slice s) asm "PLDDICT"; +cell preload_dict(slice s) pure asm "PLDDICT"; ``` Preloads a dictionary `D` from slice `s`. @@ -826,7 +853,7 @@ Preloads a dictionary `D` from slice `s`. #### skip_dict ```func -slice skip_dict(slice s) asm "SKIPDICT"; +slice skip_dict(slice s) pure asm "SKIPDICT"; ``` Loads a dictionary as `load_dict` but returns only the remainder of the slice. @@ -836,7 +863,7 @@ Loads a dictionary as `load_dict` but returns only the remainder of the slice. #### slice_refs ```func -int slice_refs(slice s) asm "SREFS"; +int slice_refs(slice s) pure asm "SREFS"; ``` Returns the number of references in slice `s`. @@ -844,7 +871,7 @@ Returns the number of references in slice `s`. #### slice_bits ```func -int slice_bits(slice s) asm "SBITS"; +int slice_bits(slice s) pure asm "SBITS"; ``` Returns the number of data bits in slice `s`. @@ -852,7 +879,7 @@ Returns the number of data bits in slice `s`. #### slice_bits_refs ```func -(int, int) slice_bits_refs(slice s) asm "SBITREFS"; +(int, int) slice_bits_refs(slice s) pure asm "SBITREFS"; ``` Returns both the number of data bits and the number of references in `s`. @@ -860,7 +887,7 @@ Returns both the number of data bits and the number of references in `s`. #### slice_empty? ```func -int slice_empty?(slice s) asm "SEMPTY"; +int slice_empty?(slice s) pure asm "SEMPTY"; ``` Checks whether slice `s` is empty (i.e., contains no bits of data and no cell references). @@ -868,7 +895,7 @@ Checks whether slice `s` is empty (i.e., contains no bits of data and no cell re #### slice_data_empty? ```func -int slice_data_empty?(slice s) asm "SDEMPTY"; +int slice_data_empty?(slice s) pure asm "SDEMPTY"; ``` Checks whether slice `s` has no bits of data. @@ -876,7 +903,7 @@ Checks whether slice `s` has no bits of data. #### slice_refs_empty? ```func -int slice_refs_empty?(slice s) asm "SREMPTY"; +int slice_refs_empty?(slice s) pure asm "SREMPTY"; ``` Checks whether slice `s` has no references. @@ -884,7 +911,7 @@ Checks whether slice `s` has no references. #### slice_depth ```func -int slice_depth(slice s) asm "SDEPTH"; +int slice_depth(slice s) pure asm "SDEPTH"; ``` 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`. @@ -898,7 +925,7 @@ All of the primitives listed below verify whether there is enough space in the ` #### begin_cell ```func -builder begin_cell() asm "NEWC"; +builder begin_cell() pure asm "NEWC"; ``` Creates a new empty `builder`. @@ -906,7 +933,7 @@ Creates a new empty `builder`. #### end_cell ```func -cell end_cell(builder b) asm "ENDC"; +cell end_cell(builder b) pure asm "ENDC"; ``` Converts `builder` into an ordinary `cell`. @@ -914,7 +941,7 @@ Converts `builder` into an ordinary `cell`. #### store_ref ```func -builder store_ref(builder b, cell c) asm(c b) "STREF"; +builder store_ref(builder b, cell c) pure asm(c b) "STREF"; ``` Stores a reference to cell `c` into builder `b`. @@ -922,7 +949,7 @@ Stores a reference to cell `c` into builder `b`. #### store_uint ```func -builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; +builder store_uint(builder b, int x, int len) pure builtin; ``` Stores an unsigned `len`-bit integer `x` into `b` for `0 ≤ len ≤ 256`. @@ -930,7 +957,7 @@ Stores an unsigned `len`-bit integer `x` into `b` for `0 ≤ len ≤ 256`. #### store_int ```func -builder store_int(builder b, int x, int len) asm(x b len) "STIX"; +builder store_int(builder b, int x, int len) pure builtin; ``` Stores a signed `len`-bit integer `x` into `b` for `0 ≤ len ≤ 257`. @@ -938,7 +965,7 @@ Stores a signed `len`-bit integer `x` into `b` for `0 ≤ len ≤ 257`. #### store_slice ```func -builder store_slice(builder b, slice s) asm "STSLICER"; +builder store_slice(builder b, slice s) pure asm "STSLICER"; ``` Stores slice `s` into builder `b`. @@ -946,13 +973,13 @@ Stores slice `s` into builder `b`. #### store_grams ```func -builder store_grams(builder b, int x) asm "STGRAMS"; +builder store_grams(builder b, int x) pure asm "STGRAMS"; ``` #### store_coins ```func -builder store_coins(builder b, int x) asm "STGRAMS"; +builder store_coins(builder b, int x) pure asm "STGRAMS"; ``` Stores (serializes) an integer `x` in the range `0..2^120 − 1` into builder `b`. The serialization of `x` consists of a 4-bit unsigned big-endian integer `l`, which is the smallest integer `l ≥ 0`, such that `x < 2^8l`, followed by an `8l`-bit unsigned big-endian representation of `x`. If `x` does not belong to the supported range, a range check exception is thrown. @@ -962,7 +989,7 @@ It is the most common way of storing Toncoins. #### store_dict ```func -builder store_dict(builder b, cell c) asm(c b) "STDICT"; +builder store_dict(builder b, cell c) pure asm(c b) "STDICT"; ``` Stores dictionary `D` represented by cell `c` or `null` into builder `b`. In other words, stores `1`-bit and a reference to `c` if `c` is not `null` and `0`-bit otherwise. @@ -970,7 +997,7 @@ Stores dictionary `D` represented by cell `c` or `null` into builder `b`. In oth #### store_maybe_ref ```func -builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; +builder store_maybe_ref(builder b, cell c) pure asm(c b) "STOPTREF"; ``` Equivalent to `store_dict`. @@ -980,7 +1007,7 @@ Equivalent to `store_dict`. #### builder_refs ```func -int builder_refs(builder b) asm "BREFS"; +int builder_refs(builder b) pure asm "BREFS"; ``` Returns the number of cell references already stored in builder `b`. @@ -988,7 +1015,7 @@ Returns the number of cell references already stored in builder `b`. #### builder_bits ```func -int builder_bits(builder b) asm "BBITS"; +int builder_bits(builder b) pure asm "BBITS"; ``` Returns the number of data bits already stored in builder `b`. @@ -996,7 +1023,7 @@ Returns the number of data bits already stored in builder `b`. #### builder_depth ```func -int builder_depth(builder b) asm "BDEPTH"; +int builder_depth(builder b) pure asm "BDEPTH"; ``` 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`. @@ -1006,7 +1033,7 @@ Returns the depth of builder `b`. If no cell references are stored in `b`, then #### cell_depth ```func -int cell_depth(cell c) asm "CDEPTH"; +int cell_depth(cell c) pure asm "CDEPTH"; ``` 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, it returns zero. @@ -1014,10 +1041,10 @@ Returns the depth of cell `c`. If `c` has no references, then return `0`; otherw #### cell_null? ```func -int cell_null?(cell c) asm "ISNULL"; +int cell_null?(cell c) pure asm "ISNULL"; ``` -Checks whether `c` is a `null`. Usually a `null`-cell represents an empty dictionary. FunC also has polymorphic `null?` built-in. (See [built-ins](/develop/func/builtins#other-primitives).) +Checks whether `c` is a `null`. Usually a `null`-cell represents an empty dictionary. FunC also has polymorphic `null?` built-in, see above. ## Dictionaries primitives @@ -1053,12 +1080,12 @@ Values within a dictionary can be stored either as a subslice within an inner di #### dict_set ```func -cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; -cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; -cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; -(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; -(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; -(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; +cell udict_set(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTUSET"; +cell idict_set(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTISET"; +cell dict_set(cell dict, int key_len, slice index, slice value) pure asm(value index dict key_len) "DICTSET"; +(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTUSET"; +(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTISET"; +(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) pure asm(value index dict key_len) "DICTSET"; ``` Sets the value associated with `key_len`-bit key `index` in dictionary `dict` to `value` (a slice) and returns the resulting dictionary. @@ -1066,10 +1093,10 @@ Sets the value associated with `key_len`-bit key `index` in dictionary `dict` to #### dict_set_ref ```func -cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; -cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; -(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; -(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; +cell idict_set_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTISETREF"; +cell udict_set_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTUSETREF"; +(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTISETREF"; +(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTUSETREF"; ``` Similar to `dict_set` but with the value set to a reference to cell `value`. @@ -1077,8 +1104,8 @@ Similar to `dict_set` but with the value set to a reference to cell `value`. #### dict_get? ```func -(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT"; -(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT"; +(slice, int) idict_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT"; +(slice, int) udict_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT"; ``` Searches for the key `index` within the `dict` dictionary, which uses keys of `key_len` bits. If successful, it retrieves the associated value as a `slice` and returns a flag value of `-1` to indicate **success**. If the search fails, it returns `(null, 0)​​`. @@ -1086,8 +1113,8 @@ Searches for the key `index` within the `dict` dictionary, which uses keys of `k #### dict_get_ref? ```func -(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF"; -(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF"; +(cell, int) idict_get_ref?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIGETREF"; +(cell, int) udict_get_ref?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUGETREF"; ``` Similar to `dict_get?` but returns the first reference of the found value. @@ -1095,7 +1122,7 @@ Similar to `dict_get?` but returns the first reference of the found value. #### dict_get_ref ```func -cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF"; +cell idict_get_ref(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIGETOPTREF"; ``` A variant of `dict_get_ref?` that returns `null` instead of the value if the key `index` is absent from the dictionary `dict`. @@ -1103,8 +1130,8 @@ A variant of `dict_get_ref?` that returns `null` instead of the value if the key #### dict_set_get_ref ```func -(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF"; -(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF"; +(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTISETGETOPTREF"; +(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTUSETGETOPTREF"; ``` Sets the value associated with `index` to `value` (if `value` is `null`, then the key is deleted instead) and returns the old value (or `null` if the value was absent). @@ -1112,8 +1139,8 @@ Sets the value associated with `index` to `value` (if `value` is `null`, then th #### dict_delete? ```func -(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL"; -(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL"; +(cell, int) idict_delete?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIDEL"; +(cell, int) udict_delete?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUDEL"; ``` Deletes `key_len`-bit key `index` from the dictionary `dict`. If the key is present, returns the modified dictionary `dict'` and the success flag `−1`. Otherwise, returns the original dictionary `dict` and `0`. @@ -1121,10 +1148,10 @@ Deletes `key_len`-bit key `index` from the dictionary `dict`. If the key is pres #### dict_delete_get? ```func -(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; -(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; -(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; -(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; ``` Deletes `key_len`-bit key `index` from dictionary `dict`. If the key is present, returns the modified dictionary `dict'`, the original value `x` associated with the key k (represented by a Slice), and the success flag `−1`. Otherwise, returns `(dict, null, 0)`. @@ -1132,8 +1159,8 @@ Deletes `key_len`-bit key `index` from dictionary `dict`. If the key is present, #### dict_add? ```func -(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD"; -(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD"; +(cell, int) udict_add?(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTUADD"; +(cell, int) idict_add?(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTIADD"; ``` An `add` counterpart of `dict_set` sets the value associated with key `index` in dictionary `dict` to `value` but only if it is not already present in `D`. Returns either modified version of the dictionary and `-1` flag or `(dict, 0)`. @@ -1141,8 +1168,8 @@ An `add` counterpart of `dict_set` sets the value associated with key `index` in #### dict_replace? ```func -(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE"; -(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE"; +(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTUREPLACE"; +(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTIREPLACE"; ``` A `replace` operation similar to `dict_set` but which sets the value of key `index` in dictionary `dict` to `value` only if the key was already present in `dict`. Returns either modified version of the dictionary and `-1` flag or `(dict, 0)`. @@ -1154,12 +1181,12 @@ The following primitives accept the new value as a builder instead of a slice, w #### dict_set_builder ```func -cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; -cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; -cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; -(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; -(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; -(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; +cell udict_set_builder(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTUSETB"; +cell idict_set_builder(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTISETB"; +cell dict_set_builder(cell dict, int key_len, slice index, builder value) pure asm(value index dict key_len) "DICTSETB"; +(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTISETB"; +(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTUSETB"; +(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) pure asm(value index dict key_len) "DICTSETB"; ``` Similar to `dict_set` but accepts a builder. @@ -1167,8 +1194,8 @@ Similar to `dict_set` but accepts a builder. #### dict_add_builder? ```func -(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB"; -(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB"; +(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTUADDB"; +(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTIADDB"; ``` Similar to `dict_add?` but accepts a builder. @@ -1176,8 +1203,8 @@ Similar to `dict_add?` but accepts a builder. #### dict_replace_builder? ```func -(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB"; -(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB"; +(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTUREPLACEB"; +(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTIREPLACEB"; ``` Similar to `dict_replace?` but accepts a builder. @@ -1185,12 +1212,12 @@ Similar to `dict_replace?` but accepts a builder. #### dict_delete_get_min ```func -(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; -(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; -(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; -(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; ``` Computes the minimum key `k` in dictionary `dict`, removes it, and returns `(dict', k, x, -1)`, where `dict'` is the modified version of `dict` and `x` is the value associated with `k`. If the dict is empty, returns `(dict, null, null, 0)`. @@ -1200,12 +1227,12 @@ Note that the key returned by `idict_delete_get_min` may differ from the key ret #### dict_delete_get_max ```func -(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; -(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; -(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; -(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; ``` Computes the maximum key `k` in dictionary `dict`, removes it, and returns `(dict', k, x, -1)`, where `dict'` is the modified version of `dict` and `x` is the value associated with `k`. If the dict is empty, returns `(dict, null, null, 0)`. @@ -1213,8 +1240,8 @@ Computes the maximum key `k` in dictionary `dict`, removes it, and returns `(dic #### dict_get_min? ```func -(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_min?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_min?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; ``` Computes the minimum key `k` in dictionary `dict`, the associated value `x`, and returns `(k, x, -1)`. If the dictionary is empty, returns `(null, null, 0)`. @@ -1222,8 +1249,8 @@ Computes the minimum key `k` in dictionary `dict`, the associated value `x`, and #### dict_get_max? ```func -(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_max?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_max?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; ``` Computes the maximum key `k` in dictionary `dict`, the associated value `x`, and returns `(k, x, -1)`. If the dictionary is empty, returns `(null, null, 0)`. @@ -1231,8 +1258,8 @@ Computes the maximum key `k` in dictionary `dict`, the associated value `x`, and #### dict_get_min_ref? ```func -(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; -(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) udict_get_min_ref?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_min_ref?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; ``` Similar to `dict_get_min?` but returns the only reference in the value as a reference. @@ -1240,8 +1267,8 @@ Similar to `dict_get_min?` but returns the only reference in the value as a refe #### dict_get_max_ref? ```func -(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; -(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; +(int, cell, int) udict_get_max_ref?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_max_ref?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; ``` Similar to `dict_get_max?` but returns the only reference in the value as a reference. @@ -1249,8 +1276,8 @@ Similar to `dict_get_max?` but returns the only reference in the value as a refe #### dict_get_next? ```func -(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; ``` Computes the minimum key `k` in dictionary `dict` that is greater than `pivot`; returns `k`, associated value, and a flag indicating success. If the dictionary is empty, returns `(null, null, 0)`. @@ -1258,8 +1285,8 @@ Computes the minimum key `k` in dictionary `dict` that is greater than `pivot`; #### dict_get_nexteq? ```func -(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; ``` Similar to `dict_get_next?` but computes the minimum key `k` that is greater than or equal to `pivot`. @@ -1267,8 +1294,8 @@ Similar to `dict_get_next?` but computes the minimum key `k` that is greater tha #### dict_get_prev? ```func -(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; ``` Similar to `dict_get_next?` but computes the maximum key `k` smaller than `pivot`. @@ -1276,8 +1303,8 @@ Similar to `dict_get_next?` but computes the maximum key `k` smaller than `pivot #### dict_get_preveq? ```func -(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; ``` Similar to `dict_get_prev?` but computes the maximum key `k` smaller than or equal to `pivot`. @@ -1285,7 +1312,7 @@ Similar to `dict_get_prev?` but computes the maximum key `k` smaller than or equ #### new_dict ```func -cell new_dict() asm "NEWDICT"; +cell new_dict() pure asm "NEWDICT"; ``` Creates an empty dictionary, which is actually a `null` value. Special case of `null()`. @@ -1293,7 +1320,7 @@ Creates an empty dictionary, which is actually a `null` value. Special case of ` #### dict_empty? ```func -int dict_empty?(cell c) asm "DICTEMPTY"; +int dict_empty?(cell c) pure asm "DICTEMPTY"; ``` Checks whether a dictionary is empty. Equivalent to `cell_null?`. @@ -1305,7 +1332,7 @@ TVM also supports dictionaries with non-fixed length keys which form a prefix co #### pfxdict_get? ```func -(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2"; +(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) pure asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2"; ``` Returns `(s', x, s'', -1)` or `(null, null, s, 0)`. @@ -1314,7 +1341,7 @@ Looks up the unique prefix of slice `key` present in the prefix code dictionary #### pfxdict_set? ```func -(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET"; +(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) pure asm(value key dict key_len) "PFXDICTSET"; ``` Similar to `dict_set` but may fail if the key is a prefix of another key presented in the dictionary. Indicating success, returns a flag. @@ -1322,7 +1349,7 @@ Similar to `dict_set` but may fail if the key is a prefix of another key present #### pfxdict_delete? ```func -(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL"; +(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) pure asm(key dict key_len) "PFXDICTDEL"; ``` Similar to `dict_delete?`. @@ -1332,25 +1359,42 @@ Similar to `dict_delete?`. #### null ```func -forall X -> X null() asm "PUSHNULL"; +forall X -> X null() pure asm "PUSHNULL"; ``` By the TVM type `Null`, FunC represents the absence of a value of some atomic type. So `null` can actually have any atomic type. +#### null? + +```func +forall X -> int null?(X x) pure builtin; +``` + +Checks whether the argument is null. + +#### touch + +```func +forall X -> X touch(X x) pure builtin; +forall X -> (X, ()) ~touch(X x) pure builtin; +``` + +Moves a variable `x` to the top of the stack. + #### ~impure_touch ```func -forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP"; +forall X -> (X, ()) ~impure_touch(X x) asm "NOP"; ``` -Mark a variable as used, such that the code which produced it won't be deleted even if it is not impure. (c.f. [impure specifier](/develop/func/functions#impure-specifier)) +Mark a variable as used, such that the code which produced it won't be deleted even if it is not impure. (c.f. [pure specifier](/develop/func/functions#pure-specifier)) -## Other primitives +## Integer operations #### min ```func -int min(int x, int y) asm "MIN"; +int min(int x, int y) pure asm "MIN"; ``` Computes the minimum of two integers `x` and `y`. @@ -1358,7 +1402,7 @@ Computes the minimum of two integers `x` and `y`. #### max ```func -int max(int x, int y) asm "MAX"; +int max(int x, int y) pure asm "MAX"; ``` Computes the maximum of two integers `x` and `y`. @@ -1366,7 +1410,7 @@ Computes the maximum of two integers `x` and `y`. #### minmax ```func -(int, int) minmax(int x, int y) asm "MINMAX"; +(int, int) minmax(int x, int y) pure asm "MINMAX"; ``` Sorts two integers. @@ -1374,7 +1418,42 @@ Sorts two integers. #### abs ```func -int abs(int x) asm "ABS"; +int abs(int x) pure asm "ABS"; ``` Computes the absolute value of the integer `x`. + +#### divmod + +```func +(int, int) divmod(int x, int y) pure builtin; +``` + +Computes the quotient and remainder of `x / y`. Example: `divmod(112,3) = (37,1)`. + +#### moddiv + +```func +(int, int) moddiv(int x, int y) pure builtin; +``` + +Computes the remainder and quotient of `x / y`. Example: `moddiv(112,3) = (1,37)`. + +#### muldiv + +```func +int muldiv(int x, int y, int z) pure builtin; +int muldivr(int x, int y, int z) pure builtin; +int muldivc(int x, int y, int z) pure builtin; +``` + +Computes multiple-then-divide: `floor(x * y / z)`, `round(x * y / z)`, `ceil(x * y / z). +The intermediate result is stored in 513-bit integer, so it won't overflow if the actual result fits into a 257-bit integer. + +#### muldivmod + +```func +(int, int) muldivmod(int x, int y, int z) pure builtin; +``` + +Computes the quotient and remainder of `x * y / z`. diff --git a/docs/develop/howto/fees-low-level.md b/docs/develop/howto/fees-low-level.md index a3cebfac59..74d199ca10 100644 --- a/docs/develop/howto/fees-low-level.md +++ b/docs/develop/howto/fees-low-level.md @@ -155,7 +155,7 @@ Just an example of how proper cell work may substantially decrease gas costs. Let's imagine that you want to add some encoded payload to the outgoing message. Straightforward implementation will be as follows: -```cpp +```func slice payload_encoding(int a, int b, int c) { return begin_cell().store_uint(a,8) @@ -164,15 +164,15 @@ slice payload_encoding(int a, int b, int c) { .end_cell().begin_parse(); } -() send_message(slice destination) impure { +() send_message(slice destination) { slice payload = payload_encoding(1, 7, 12); var msg = begin_cell() .store_uint(0x18, 6) .store_slice(destination) .store_coins(0) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - .store_uint(0x33bbff77, 32) ;; op-code (see smart-contract guidelines) - .store_uint(cur_lt(), 64) ;; query_id (see smart-contract guidelines) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) + .store_uint(0x33bbff77, 32) // op-code (see smart-contract guidelines) + .store_uint(cur_lt(), 64) // query_id (see smart-contract guidelines) .store_slice(payload) .end_cell(); send_raw_message(msg, 64); @@ -181,8 +181,8 @@ slice payload_encoding(int a, int b, int c) { What is the problem with this code? `payload_encoding` to generate a slice bit-string, first create a cell via `end_cell()` (+500 gas units). Then parse it `begin_parse()` (+100 gas units). The same code can be written without those unnecessary operations by changing some commonly used types: -```cpp -;; we add asm for function which stores one builder to the another, which is absent from stdlib +```func +// we add asm for function which stores one builder to the another, which is absent from stdlib builder store_builder(builder to, builder what) asm(what to) "STB"; builder payload_encoding(int a, int b, int c) { @@ -192,15 +192,15 @@ builder payload_encoding(int a, int b, int c) { .store_uint(c,8); } -() send_message(slice destination) impure { +() send_message(slice destination) { builder payload = payload_encoding(1, 7, 12); var msg = begin_cell() .store_uint(0x18, 6) .store_slice(destination) .store_coins(0) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - .store_uint(0x33bbff77, 32) ;; op-code (see smart-contract guidelines) - .store_uint(cur_lt(), 64) ;; query_id (see smart-contract guidelines) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) + .store_uint(0x33bbff77, 32) // op-code (see smart-contract guidelines) + .store_uint(cur_lt(), 64) // query_id (see smart-contract guidelines) .store_builder(payload) .end_cell(); send_raw_message(msg, 64); @@ -224,7 +224,7 @@ Dictionaries on TON are introduced as trees (DAGs to be precise) of cells. That Note that FunC manipulates stack entries under the hood. That means that the code: -```cpp +```func (int a, int b, int c) = some_f(); return (c, b, a); ``` @@ -307,4 +307,4 @@ For educational purposes [example of the old one](https://explorer.toncoin.org/c * [TON Fees overview](/develop/smart-contracts/fees) * [Transactions and Phases](/learn/tvm-instructions/tvm-overview#transactions-and-phases) -* [Fees calculation](/develop/smart-contracts/fee-calculation) \ No newline at end of file +* [Fees calculation](/develop/smart-contracts/fee-calculation) diff --git a/docs/develop/howto/step-by-step.md b/docs/develop/howto/step-by-step.md index 7e38bb32a4..105f3e8c15 100644 --- a/docs/develop/howto/step-by-step.md +++ b/docs/develop/howto/step-by-step.md @@ -79,7 +79,7 @@ You will see something like this: :::info Please note here and further that the code, comments, and/or documentation may contain parameters, methods, and definitions such as “gram”, “nanogram”, etc. That is a legacy of the original TON code, developed by Telegram. Gram cryptocurrency was never issued. The currency of TON is Toncoin and the currency of TON testnet is Test Toncoin. ::: -```cpp +``` got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2075):BFE876CE2085274FEDAF1BD80F3ACE50F42B5A027DF230AD66DCED1F09FB39A7:522C027A721FABCB32574E3A809ABFBEE6A71DE929C1FA2B1CD0DDECF3056505 account state is (account addr:(addr_std @@ -139,7 +139,7 @@ One such tool is the Fift interpreter, which is included in this distribution an Consider the file `new-wallet.fif` (usually located at `crypto/smartcont/new-wallet.fif` with respect to the source directory) containing the source of a simple wallet smart contract: -```cpp +```fift #!/usr/bin/env fift -s "TonUtil.fif" include "Asm.fif" include @@ -224,7 +224,7 @@ instead of indicating the complete search paths in the command line. If everything worked, you'll see something like this: -```cpp +``` Creating new wallet in workchain 0 Saved new private key to file my_wallet_name.pk StateInit: x{34_} @@ -291,7 +291,7 @@ as explained above in Section 2. The only number you need from the output is the producing the correct value 39445 = 0x9A15: -```cpp +``` got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2240):18E6DA7707191E76C71EABBC5277650666B7E2CFA2AEF2CE607EAFE8657A3820:4EFA2540C5D1E4A1BA2B529EE0B65415DF46BFFBD27A8EB74C4C0E17770D03B1 creating VM starting VM to run method `seqno` (85143) of smart contract -1:FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 @@ -302,7 +302,7 @@ result: [ 39445 ] Next, you create an external message to the test giver asking it to send another message to your (uninitialized) smart contract carrying a specified amount of test Toncoin. There is a special Fift script for generating this external message located at `crypto/smartcont/testgiver.fif`: -```cpp +```fift #!/usr/bin/env fift -s "TonUtil.fif" include @@ -366,7 +366,7 @@ This Fift code creates an internal message from the test giver smart contract to The external message is serialized and saved into the file `wallet-query.boc`. Some output is generated in the process: -```cpp +``` Test giver address = -1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260 kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny Requesting GR$6.666 to account 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb = 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 seqno=0x9a15 bounce=0 @@ -403,7 +403,7 @@ which means that the external message has been delivered to the collator pool. A (If you forget to type `last`, you are likely to see the unchanged state of the test giver smart contract.) The resulting output will be: -```cpp +``` got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2240):18E6DA7707191E76C71EABBC5277650666B7E2CFA2AEF2CE607EAFE8657A3820:4EFA2540C5D1E4A1BA2B529EE0B65415DF46BFFBD27A8EB74C4C0E17770D03B1 account state is (account addr:(addr_std @@ -444,7 +444,7 @@ You may notice that the sequence number stored in the persistent data has change Now we can inspect the state of our new smart contract: -```cpp +``` > getaccount 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb or > getaccount 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 @@ -452,7 +452,7 @@ or Now we see: -```cpp +``` got account state for 0:2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2 with respect to blocks (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB and (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB account state is (account addr:(addr_std @@ -479,7 +479,7 @@ Our new smart contract has some positive balance (of 6.666 test Toncoin) but has Now you can finally upload the external message with the `StateInit` of the new smart contract which contains its code and data: -```cpp +``` > sendfile my_wallet_name-query.boc ... external message status is 1 > last @@ -530,7 +530,7 @@ Actually, the simple wallet smart contract used in this example can be used to t An example of how you might use this smart contract is provided in the sample file `crypto/smartcont/wallet.fif` : -```cpp +```fift #!/usr/bin/env fift -s "TonUtil.fif" include diff --git a/docs/develop/howto/subresolvers.md b/docs/develop/howto/subresolvers.md index e25e6eabf3..4430ed7ab1 100644 --- a/docs/develop/howto/subresolvers.md +++ b/docs/develop/howto/subresolvers.md @@ -28,30 +28,30 @@ You can attempt to see the resolver code by going to `resolve-contract.ton.resol Some repeated parts are omitted. ```func -(int, cell) dnsresolve(slice subdomain, int category) method_id { +get (int, cell) dnsresolve(slice subdomain, int category) { int subdomain_bits = slice_bits(subdomain); throw_unless(70, (subdomain_bits % 8) == 0); - int starts_with_zero_byte = subdomain.preload_int(8) == 0; ;; assuming that 'subdomain' is not empty + int starts_with_zero_byte = subdomain.preload_int(8) == 0; // assuming that 'subdomain' is not empty if (starts_with_zero_byte) { subdomain~load_uint(8); - if (subdomain.slice_bits() == 0) { ;; current contract has no DNS records by itself + if (subdomain.slice_bits() == 0) { // current contract has no DNS records by itself return (8, null()); } } - ;; we are loading some subdomain - ;; supported subdomains are "ton\\0", "me\\0t\\0" and "address\\0" + // we are loading some subdomain + // supported subdomains are "ton\\0", "me\\0t\\0" and "address\\0" slice subdomain_sfx = null(); builder domain_nft_address = null(); if (subdomain.starts_with("746F6E00"s)) { - ;; we're resolving - ;; "ton" \\0 \\0 [subdomain_sfx] + // we're resolving + // "ton" \\0 \\0 [subdomain_sfx] subdomain~skip_bits(32); - ;; reading domain name + // reading domain name subdomain_sfx = subdomain; while (subdomain_sfx~load_uint(8)) { } @@ -72,17 +72,17 @@ Some repeated parts are omitted. } if (slice_empty?(subdomain_sfx)) { - ;; example of domain being resolved: - ;; [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" - ;; [what is accessible by this contract] "ton\\0ratelance\\0" - ;; subdomain "ratelance" - ;; subdomain_sfx "" + // example of domain being resolved: + // [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + // [what is accessible by this contract] "ton\\0ratelance\\0" + // subdomain "ratelance" + // subdomain_sfx "" - ;; we want the resolve result to point at contract of 'ratelance.ton', not its owner - ;; so we must answer that resolution is complete + "wallet"H is address of 'ratelance.ton' contract + // we want the resolve result to point at contract of 'ratelance.ton', not its owner + // so we must answer that resolution is complete + "wallet"H is address of 'ratelance.ton' contract - ;; dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; - ;; _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; + // dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; + // _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; cell wallet_record = begin_cell().store_uint(0x9fd3, 16).store_builder(domain_nft_address).store_uint(0, 8).end_cell(); @@ -96,12 +96,12 @@ Some repeated parts are omitted. return (subdomain_bits, null()); } } else { - ;; subdomain "resolve-contract" - ;; subdomain_sfx "ton\\0ratelance\\0" - ;; we want to pass \\0 further, so that next resolver has opportunity to process only one byte + // subdomain "resolve-contract" + // subdomain_sfx "ton\\0ratelance\\0" + // we want to pass \\0 further, so that next resolver has opportunity to process only one byte - ;; next resolver is contract of 'resolve-contract<.ton>' - ;; dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; + // next resolver is contract of 'resolve-contract<.ton>' + // dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; cell resolver_record = begin_cell().store_uint(0xba93, 16).store_builder(domain_nft_address).end_cell(); return (subdomain_bits - slice_bits(subdomain_sfx) - 8, resolver_record); } @@ -165,12 +165,12 @@ We need to store owner's address and the *domain*->*record hash*->*record value* global slice owner; global cell domains; -() load_data() impure { +() load_data() { slice ds = get_data().begin_parse(); owner = ds~load_msg_addr(); domains = ds~load_dict(); } -() save_data() impure { +() save_data() { set_data(begin_cell().store_slice(owner).store_dict(domains).end_cell()); } ``` @@ -179,14 +179,14 @@ global cell domains; ```func const int op::update_record = 0x537a3491; -;; op::update_record#537a3491 domain_name:^Cell record_key:uint256 -;; value:(Maybe ^Cell) = InMsgBody; +// op::update_record#537a3491 domain_name:^Cell record_key:uint256 +// value:(Maybe ^Cell) = InMsgBody; () recv_internal(cell in_msg, slice in_msg_body) { - if (in_msg_body.slice_empty?()) { return (); } ;; simple money transfer + if (in_msg_body.slice_empty?()) { return (); } // simple money transfer slice in_msg_full = in_msg.begin_parse(); - if (in_msg_full~load_uint(4) & 1) { return (); } ;; bounced message + if (in_msg_full~load_uint(4) & 1) { return (); } // bounced message slice sender = in_msg_full~load_msg_addr(); load_data(); @@ -198,7 +198,7 @@ const int op::update_record = 0x537a3491; (cell records, _) = domains.udict_get_ref?(256, string_hash(domain)); int key = in_msg_body~load_uint(256); - throw_if(502, key == 0); ;; cannot update "all records" record + throw_if(502, key == 0); // cannot update "all records" record if (in_msg_body~load_uint(1) == 1) { cell value = in_msg_body~load_ref(); @@ -223,31 +223,31 @@ After that, we update the record for the specified domain and save new data into ```func (slice, slice) ~parse_sd(slice subdomain) { - ;; "test\0qwerty\0" -> "test" "qwerty\0" + // "test\0qwerty\0" -> "test" "qwerty\0" slice subdomain_sfx = subdomain; - while (subdomain_sfx~load_uint(8)) { } ;; searching zero byte + while (subdomain_sfx~load_uint(8)) { } // searching zero byte subdomain~skip_last_bits(slice_bits(subdomain_sfx)); return (subdomain, subdomain_sfx); } -(int, cell) dnsresolve(slice subdomain, int category) method_id { +get (int, cell) dnsresolve(slice subdomain, int category) { int subdomain_bits = slice_bits(subdomain); throw_unless(70, subdomain_bits % 8 == 0); if (subdomain.preload_uint(8) == 0) { subdomain~skip_bits(8); } - slice subdomain_suffix = subdomain~parse_sd(); ;; "test\0" -> "test" "" + slice subdomain_suffix = subdomain~parse_sd(); // "test\0" -> "test" "" int subdomain_suffix_bits = slice_bits(subdomain_suffix); load_data(); (cell records, _) = domains.udict_get_ref?(256, string_hash(subdomain)); - if (subdomain_suffix_bits > 0) { ;; more than "\0" requested + if (subdomain_suffix_bits > 0) { // more than "\0" requested category = "dns_next_resolver"H; } int resolved = subdomain_bits - subdomain_suffix_bits; - if (category == 0) { ;; all categories are requested + if (category == 0) { // all categories are requested return (resolved, records); } @@ -339,36 +339,36 @@ builder get_tme_nft_address_by_index(int index) inline { return (readable, target); } -slice decode_base64_address(slice readable) method_id { +get slice decode_base64_address(slice readable) { (slice _remaining, builder addr) = decode_base64_address_to(readable, begin_cell()); return addr.end_cell().begin_parse(); } -(int, cell) dnsresolve(slice subdomain, int category) method_id { +get (int, cell) dnsresolve(slice subdomain, int category) { int subdomain_bits = slice_bits(subdomain); throw_unless(70, (subdomain_bits % 8) == 0); - int starts_with_zero_byte = subdomain.preload_int(8) == 0; ;; assuming that 'subdomain' is not empty + int starts_with_zero_byte = subdomain.preload_int(8) == 0; // assuming that 'subdomain' is not empty if (starts_with_zero_byte) { subdomain~load_uint(8); - if (subdomain.slice_bits() == 0) { ;; current contract has no DNS records by itself + if (subdomain.slice_bits() == 0) { // current contract has no DNS records by itself return (8, null()); } } - ;; we are loading some subdomain - ;; supported subdomains are "ton\\0", "me\\0t\\0" and "address\\0" + // we are loading some subdomain + // supported subdomains are "ton\\0", "me\\0t\\0" and "address\\0" slice subdomain_sfx = null(); builder domain_nft_address = null(); if (subdomain.starts_with("746F6E00"s)) { - ;; we're resolving - ;; "ton" \\0 \\0 [subdomain_sfx] + // we're resolving + // "ton" \\0 \\0 [subdomain_sfx] subdomain~skip_bits(32); - ;; reading domain name + // reading domain name subdomain_sfx = subdomain; while (subdomain_sfx~load_uint(8)) { } @@ -376,10 +376,10 @@ slice decode_base64_address(slice readable) method_id { domain_nft_address = get_ton_dns_nft_address_by_index(slice_hash(subdomain)); } elseif (subdomain.starts_with("6D65007400"s)) { - ;; "t" \\0 "me" \\0 \\0 [subdomain_sfx] + // "t" \\0 "me" \\0 \\0 [subdomain_sfx] subdomain~skip_bits(40); - ;; reading domain name + // reading domain name subdomain_sfx = subdomain; while (subdomain_sfx~load_uint(8)) { } @@ -400,17 +400,17 @@ slice decode_base64_address(slice readable) method_id { } if (slice_empty?(subdomain_sfx)) { - ;; example of domain being resolved: - ;; [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" - ;; [what is accessible by this contract] "ton\\0ratelance\\0" - ;; subdomain "ratelance" - ;; subdomain_sfx "" + // example of domain being resolved: + // [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + // [what is accessible by this contract] "ton\\0ratelance\\0" + // subdomain "ratelance" + // subdomain_sfx "" - ;; we want the resolve result to point at contract of 'ratelance.ton', not its owner - ;; so we must answer that resolution is complete + "wallet"H is address of 'ratelance.ton' contract + // we want the resolve result to point at contract of 'ratelance.ton', not its owner + // so we must answer that resolution is complete + "wallet"H is address of 'ratelance.ton' contract - ;; dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; - ;; _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; + // dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; + // _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; cell wallet_record = begin_cell().store_uint(0x9fd3, 16).store_builder(domain_nft_address).store_uint(0, 8).end_cell(); @@ -424,15 +424,15 @@ slice decode_base64_address(slice readable) method_id { return (subdomain_bits, null()); } } else { - ;; example of domain being resolved: - ;; [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0resolve-contract\\0ton\\0ratelance\\0" - ;; [what is accessible by this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" - ;; subdomain "resolve-contract" - ;; subdomain_sfx "ton\\0ratelance\\0" - ;; and we want to pass \\0 further, so that next resolver has opportunity to process only one byte + // example of domain being resolved: + // [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0resolve-contract\\0ton\\0ratelance\\0" + // [what is accessible by this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + // subdomain "resolve-contract" + // subdomain_sfx "ton\\0ratelance\\0" + // and we want to pass \\0 further, so that next resolver has opportunity to process only one byte - ;; next resolver is contract of 'resolve-contract<.ton>' - ;; dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; + // next resolver is contract of 'resolve-contract<.ton>' + // dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; cell resolver_record = begin_cell().store_uint(0xba93, 16).store_builder(domain_nft_address).end_cell(); return (subdomain_bits - slice_bits(subdomain_sfx) - 8, resolver_record); } diff --git a/docs/develop/network/adnl-tcp.md b/docs/develop/network/adnl-tcp.md index 58375a689a..70b51bf524 100644 --- a/docs/develop/network/adnl-tcp.md +++ b/docs/develop/network/adnl-tcp.md @@ -171,7 +171,7 @@ Let's analyze the call and getting the result from the `a2` method of the contra Method code in FunC: ```func -(cell, cell) a2() method_id { +get (cell, cell) a2() { cell a = begin_cell().store_uint(0xAABBCC8, 32).end_cell(); cell b = begin_cell().store_uint(0xCCFFCC1, 32).end_cell(); return (a, b); diff --git a/docs/develop/oracles/red_stone.mdx b/docs/develop/oracles/red_stone.mdx index 9a4851a446..3d8ab4002b 100644 --- a/docs/develop/oracles/red_stone.mdx +++ b/docs/develop/oracles/red_stone.mdx @@ -60,7 +60,7 @@ See [TON RedStone payload packing](#-ton-redstone-payload-packing) section below #### get_prices ```func -(cell) get_prices_v2(cell data_feed_ids, cell payload) method_id; +get (cell) get_prices_v2(cell data_feed_ids, cell payload); ``` The function process on-chain the `payload` passed as an argument @@ -68,7 +68,7 @@ and returns a `cell` of aggregated values of each feed passed as an identifier i Due to HTTP GET method length limitation in TON API v4, the function is written for TON API v2. -That are just a `method_id` functions - they don't modify the contract's storage and don't consume TONs. +That are just `get` functions - they don't modify the contract's storage and don't consume TONs. #### OP_REDSTONE_WRITE_PRICES @@ -100,7 +100,7 @@ See how it works on: https://ton-showroom.redstone.finance/ #### read_prices ```func -(tuple) read_prices(tuple data_feed_ids) method_id; +get (tuple) read_prices(tuple data_feed_ids); ``` The function reads the values persisting in the contract's storage and returns a tuple corresponding to the @@ -108,17 +108,17 @@ passed `feed_ids`. The function doesn't modify the storage and can read only aggregated values of the `feed_ids` saved by using `write_prices` function. -That's just a `method_id` function - it doesn't modify the contract's storage and don't consume TONs. +That's just a `get` function - it doesn't modify the contract's storage and don't consume TONs. #### read_timestamp ```func -(int) read_timestamp() method_id; +get (int) read_timestamp(); ``` Returns the timestamp of data last saved/written to the contract's storage by using `OP_REDSTONE_WRITE_PRICES` message. -That's just a `method_id` function - it doesn't modify the contract's storage and don't consume TONs. +That's just a `get` function - it doesn't modify the contract's storage and don't consume TONs. ### price_feed.fc @@ -179,12 +179,12 @@ That's an internal message - it consumes GAS and modifies the contract's storage #### get_price_and_timestamp ```func -(int, int) get_price_and_timestamp() method_id; +get (int, int) get_price_and_timestamp(); ``` Returns the value and timestamp of the last saved/written data to the adapter's storage by sending `OP_REDSTONE_FETCH_DATA` message and fetching the returned value of the `OP_REDSTONE_DATA_FETCHED` message. -That's just a `method_id` function - it doesn't modify the contract's storage and don't consume TONs. +That's just a `get` function - it doesn't modify the contract's storage and don't consume TONs. ### single_feed_man.fc @@ -211,7 +211,7 @@ the single feed only, to omit the communication needs between feed and manager c #### get_price ```func -(int, int) get_price(cell payload) method_id; +get (int, int) get_price(cell payload); ``` Similar to `get_prices`, but omitting the first (`data_feed_ids`) argument as have it configured during @@ -220,7 +220,7 @@ the initialization. Returns also the min timestamp of the passed data packages. #### read_price_and_timestamp ```func -(int, int) read_price_and_timestamp() method_id; +get (int, int) read_price_and_timestamp(); ``` Works as the `get_price_and_timestamp` function. diff --git a/docs/develop/smart-contracts/compile/README.md b/docs/develop/smart-contracts/compile/README.md index 12ca4f5f98..71ea711ed0 100644 --- a/docs/develop/smart-contracts/compile/README.md +++ b/docs/develop/smart-contracts/compile/README.md @@ -69,7 +69,7 @@ npm install ton-compiler This packages adds `ton-compiler` binary to a project. -FunC compilation is a multi-stage process. One is compiling Func to Fift code that is then compiled to a binary representation. Fift compiler already has Asm.fif bundled. +FunC compilation is a multi-stage process. One is compiling FunC to Fift code that is then compiled to a binary representation. Fift compiler already has Asm.fif bundled. FunC stdlib is bundled but could be disabled at runtime. diff --git a/docs/develop/smart-contracts/environment/installation.md b/docs/develop/smart-contracts/environment/installation.md index 1669ea2df1..a999fc0fb9 100644 --- a/docs/develop/smart-contracts/environment/installation.md +++ b/docs/develop/smart-contracts/environment/installation.md @@ -31,7 +31,7 @@ You can download and set them up below, or read this article from TON Society: Download the binaries from the table below. Make sure to select the correct version for your operating system and to install any additional dependencies: -| OS | TON binaries | fift | func | lite-client | Additional dependencies | +| OS | TON binaries | fift | FunC | lite-client | Additional dependencies | |----------------|-------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|-------------|--------------------------------------------------------------------------------------| | MacOS x86-64 | [download](https://github.com/ton-blockchain/ton/releases/latest/download/ton-mac-x86-64.zip) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/fift-mac-x86-64) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/func-mac-x86-64) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/lite-client-mac-x86-64) | | | MacOS arm64 | [download](https://github.com/ton-blockchain/ton/releases/latest/download/ton-mac-arm64.zip) | ||| `brew install openssl ninja libmicrohttpd pkg-config` | diff --git a/docs/develop/smart-contracts/fee-calculation.md b/docs/develop/smart-contracts/fee-calculation.md index 517e846c8b..8e50dfc829 100644 --- a/docs/develop/smart-contracts/fee-calculation.md +++ b/docs/develop/smart-contracts/fee-calculation.md @@ -38,29 +38,29 @@ In particular, it deduplicates data: if there are several equivalent sub-cells r Each contract has its balance. You can calculate how many TONs your contract requires to remain valid for a specified `seconds` time using the function: ```func -int get_storage_fee(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEFEE"; +int get_storage_fee(int workchain, int seconds, int bits, int cells) pure asm(cells bits seconds workchain) "GETSTORAGEFEE"; ``` You can then hardcode that value into the contract and calculate the current storage fee using: ```func -;; functions from func stdlib (not presented on mainnet) -() raw_reserve(int amount, int mode) impure asm "RAWRESERVE"; -int get_storage_fee(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEFEE"; -int my_storage_due() asm "DUEPAYMENT"; +// functions from func stdlib (not presented on mainnet) +() raw_reserve(int amount, int mode) asm "RAWRESERVE"; +int get_storage_fee(int workchain, int seconds, int bits, int cells) pure asm(cells bits seconds workchain) "GETSTORAGEFEE"; +int my_storage_due() pure asm "DUEPAYMENT"; -;; constants from stdlib -;;; Creates an output action which would reserve exactly x nanograms (if y = 0). +// constants from stdlib +/// Creates an output action which would reserve exactly x nanograms (if y = 0). const int RESERVE_REGULAR = 0; -;;; Creates an output action which would reserve at most x nanograms (if y = 2). -;;; Bit +2 in y means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. +/// Creates an output action which would reserve at most x nanograms (if y = 2). +/// Bit +2 in y means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. const int RESERVE_AT_MOST = 2; -;;; in the case of action fail - bounce transaction. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages +/// in the case of action fail - bounce transaction. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages const int RESERVE_BOUNCE_ON_ACTION_FAIL = 16; () calculate_and_reserve_storage_fee(int balance, int msg_value, int workchain, int seconds, int bits, int cells) inline { int to_leave_on_balance = my_ton_balance - msg_value + my_storage_due(); - int min_storage_fee = get_storage_fee(workchain, seconds, bits, cells); ;; can be hardcoded IF CODE OF THE CONTRACT WILL NOT BE UPDATED + int min_storage_fee = get_storage_fee(workchain, seconds, bits, cells); // can be hardcoded IF CODE OF THE CONTRACT WILL NOT BE UPDATED raw_reserve(max(to_leave_on_balance, min_storage_fee), RESERVE_AT_MOST); } ``` @@ -81,7 +81,7 @@ In most cases use the `GETGASFEE` opcode with the following parameters: ### Calculation Flow ```func -int get_compute_fee(int workchain, int gas_used) asm(gas_used workchain) "GETGASFEE"; +int get_compute_fee(int workchain, int gas_used) pure asm(gas_used workchain) "GETGASFEE"; ``` But how do you get `gas_used`? Through tests! @@ -218,17 +218,17 @@ Modes affect the fee calculation as follows: It creates an output action and returns a fee for creating a message. However, it uses an unpredictable amount of gas, which can't be calculated using formulas, so how can it be calculated? Use `GASCONSUMED`: ```func -int send_message(cell msg, int mode) impure asm "SENDMSG"; -int gas_consumed() asm "GASCONSUMED"; -;; ... some code ... +int send_message(cell msg, int mode) asm "SENDMSG"; +int gas_consumed() pure asm "GASCONSUMED"; +// ... some code ... () calculate_forward_fee(cell msg, int mode) inline { int gas_before = gas_consumed(); int forward_fee = send_message(msg, mode); int gas_usage = gas_consumed() - gas_before; - ;; forward fee -- fee value - ;; gas_usage -- amount of gas, used to send msg + // forward fee -- fee value + // gas_usage -- amount of gas, used to send msg } ``` diff --git a/docs/develop/smart-contracts/guidelines/get-methods.md b/docs/develop/smart-contracts/guidelines/get-methods.md index 6967ab5b99..d4b2e58019 100644 --- a/docs/develop/smart-contracts/guidelines/get-methods.md +++ b/docs/develop/smart-contracts/guidelines/get-methods.md @@ -6,7 +6,7 @@ Before proceeding, it is recommended that readers have a basic understanding of ## Introduction -Get methods are special functions in smart contracts that are made for querying specific data from them. Their execution doesn't cost any fees and happens outside of the blockchain. +Get methods are special functions in smart contracts that are made for querying specific data from them. Their execution doesn't cost any fees and happens outside the blockchain. These functions are very common for most smart contracts. For example, the default [Wallet contract](/participate/wallets/contracts) has several get methods, such as `seqno()`, `get_subwallet_id()` and `get_public_key()`. They are used by wallets, SDKs and APIs to fetch data about wallets. @@ -19,7 +19,7 @@ These functions are very common for most smart contracts. For example, the defau Example: ```func - int get_balance() method_id { + get int balance() { return get_data().begin_parse().preload_uint(64); } ``` @@ -29,7 +29,7 @@ These functions are very common for most smart contracts. For example, the defau Example: ```func - (int, slice, slice, cell) get_wallet_data() method_id { + get (int, slice, slice, cell) wallet_data() { return load_data(); } ``` @@ -41,7 +41,7 @@ These functions are very common for most smart contracts. For example, the defau Example: ```func - slice get_wallet_address(slice owner_address) method_id { + get slice wallet_address(slice owner_address) { (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); } @@ -52,7 +52,7 @@ These functions are very common for most smart contracts. For example, the defau Example: ```func - (int) get_ready_to_be_used() method_id { + get (int) ready_to_be_used() { int ready? = now() >= 1686459600; return ready?; } @@ -65,7 +65,7 @@ These functions are very common for most smart contracts. For example, the defau #### seqno() ```func -int seqno() method_id { +get int seqno() { return get_data().begin_parse().preload_uint(32); } ``` @@ -75,7 +75,7 @@ Returns the sequence number of the transaction within a specific wallet. This me #### get_subwallet_id() ```func -int get_subwallet_id() method_id { +get int get_subwallet_id() { return get_data().begin_parse().skip_bits(32).preload_uint(32); } ``` @@ -85,7 +85,7 @@ int get_subwallet_id() method_id { #### get_public_key() ```func -int get_public_key() method_id { +get int get_public_key() { var cs = get_data().begin_parse().skip_bits(64); return cs.preload_uint(256); } @@ -98,7 +98,7 @@ Retrieves the public key associated with the wallet. #### get_wallet_data() ```func -(int, slice, slice, cell) get_wallet_data() method_id { +get (int, slice, slice, cell) get_wallet_data() { return load_data(); } ``` @@ -113,7 +113,7 @@ This method returns the complete set of data associated with a jetton wallet: #### get_jetton_data() ```func -(int, int, slice, cell, cell) get_jetton_data() method_id { +get (int, int, slice, cell, cell) get_jetton_data() { (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); return (total_supply, -1, admin_address, content, jetton_wallet_code); } @@ -124,7 +124,7 @@ Returns data of a jetton master, including its total supply, the address of its #### get_wallet_address(slice owner_address) ```func -slice get_wallet_address(slice owner_address) method_id { +get slice get_wallet_address(slice owner_address) { (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); } @@ -137,7 +137,7 @@ Given the address of the owner, this method calculates and returns the address f #### get_nft_data() ```func -(int, int, slice, slice, cell) get_nft_data() method_id { +get (int, int, slice, slice, cell) get_nft_data() { (int init?, int index, slice collection_address, slice owner_address, cell content) = load_data(); return (init?, index, collection_address, owner_address, content); } @@ -148,7 +148,7 @@ Returns the data associated with a non-fungible token, including whether it has #### get_collection_data() ```func -(int, cell, slice) get_collection_data() method_id { +get (int, cell, slice) get_collection_data() { var (owner_address, next_item_index, content, _, _) = load_data(); slice cs = content.begin_parse(); return (next_item_index, cs~load_ref(), owner_address); @@ -160,7 +160,7 @@ Returns the data of a NFT collection, including the index of the next item to mi #### get_nft_address_by_index(int index) ```func -slice get_nft_address_by_index(int index) method_id { +get slice get_nft_address_by_index(int index) { var (_, _, _, nft_item_code, _) = load_data(); cell state_init = calculate_nft_item_state_init(index, nft_item_code); return calculate_nft_item_address(workchain(), state_init); @@ -172,7 +172,7 @@ Given an index, this method calculates and returns the address of the correspond #### royalty_params() ```func -(int, int, slice) royalty_params() method_id { +get (int, int, slice) royalty_params() { var (_, _, _, _, royalty) = load_data(); slice rs = royalty.begin_parse(); return (rs~load_uint(16), rs~load_uint(16), rs~load_msg_addr()); @@ -184,13 +184,13 @@ Fetches the royalty parameters for an NFT. These parameters include the royalty #### get_nft_content(int index, cell individual_nft_content) ```func -cell get_nft_content(int index, cell individual_nft_content) method_id { +get cell get_nft_content(int index, cell individual_nft_content) { var (_, _, content, _, _) = load_data(); slice cs = content.begin_parse(); cs~load_ref(); slice common_content = cs~load_ref().begin_parse(); return (begin_cell() - .store_uint(1, 8) ;; offchain tag + .store_uint(1, 8) // offchain tag .store_slice(common_content) .store_ref(individual_nft_content) .end_cell()); @@ -225,8 +225,8 @@ We will use Javascript libraries and tools for the examples below: Let's say there is some contract with a following get method: ```func -(int) get_total() method_id { - return get_data().begin_parse().preload_uint(32); ;; load and return the 32-bit number from the data +get (int) total() { + return get_data().begin_parse().preload_uint(32); // load and return the 32-bit number from the data } ``` @@ -246,7 +246,7 @@ async function main() { // Call get method const result = await client.runMethod( Address.parse('EQD4eA1SdQOivBbTczzElFmfiKu4SXNL4S29TReQwzzr_70k'), - 'get_total' + 'total' ); const total = result.stack.readNumber(); console.log('Total:', total); @@ -265,7 +265,7 @@ At first, you need to add a special method in contract wrapper that will execute ```ts async getTotal(provider: ContractProvider) { - const result = (await provider.get('get_total', [])).stack; + const result = (await provider.get('total', [])).stack; return result.readNumber(); } ``` @@ -313,11 +313,11 @@ Let's consider a simple example: ```func #include "imports/stdlib.fc"; -int get_total() method_id { +get int get_total() { return get_data().begin_parse().preload_uint(32); } -() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure { +() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) { if (in_msg_body.slice_bits() < 32) { return (); } @@ -326,23 +326,23 @@ int get_total() method_id { cs~skip_bits(4); slice sender = cs~load_msg_addr(); - int op = in_msg_body~load_uint(32); ;; load the operation code + int op = in_msg_body~load_uint(32); // load the operation code - if (op == 1) { ;; increase and update the number + if (op == 1) { // increase and update the number int number = in_msg_body~load_uint(32); int total = get_total(); total += number; set_data(begin_cell().store_uint(total, 32).end_cell()); } - elseif (op == 2) { ;; query the number + elseif (op == 2) { // query the number int total = get_total(); send_raw_message(begin_cell() .store_uint(0x18, 6) .store_slice(sender) .store_coins(0) - .store_uint(0, 107) ;; default message headers (see sending messages page) - .store_uint(3, 32) ;; response operation code - .store_uint(total, 32) ;; the requested number + .store_uint(0, 107) // default message headers (see sending messages page) + .store_uint(3, 32) // response operation code + .store_uint(total, 32) // the requested number .end_cell(), 64); } } diff --git a/docs/develop/smart-contracts/guidelines/random-number-generation.md b/docs/develop/smart-contracts/guidelines/random-number-generation.md index e2e77df6fe..57f0984ef4 100644 --- a/docs/develop/smart-contracts/guidelines/random-number-generation.md +++ b/docs/develop/smart-contracts/guidelines/random-number-generation.md @@ -20,7 +20,7 @@ Just add the `randomize_lt()` call before generating random numbers, and your ra ```func randomize_lt(); -int x = random(); ;; users can't predict this number +int x = random(); // users can't predict this number ``` However, you should note that validators or collators may still affect the result of the random number, as they determine the seed of the current block. @@ -40,53 +40,53 @@ Do not use this example contract in real projects, write your own instead. Let's write a simple lottery contract as an example. A user will send 1 TON to it, and with a 50% chance, will get 2 TON back. ```func -;; set the echo-contract address +// set the echo-contract address const echo_address = "Ef8Nb7157K5bVxNKAvIWreRcF0RcUlzcCA7lwmewWVNtqM3s"a; -() recv_internal (int msg_value, cell in_msg_full, slice in_msg_body) impure { +() recv_internal (int msg_value, cell in_msg_full, slice in_msg_body) { var cs = in_msg_full.begin_parse(); var flags = cs~load_uint(4); - if (flags & 1) { ;; ignore bounced messages + if (flags & 1) { // ignore bounced messages return (); } slice sender = cs~load_msg_addr(); int op = in_msg_body~load_uint(32); - if ((op == 0) & equal_slice_bits(in_msg_body, "bet")) { ;; bet from user - throw_unless(501, msg_value == 1000000000); ;; 1 TON + if ((op == 0) & equal_slice_bits(in_msg_body, "bet")) { // bet from user + throw_unless(501, msg_value == 1000000000); // 1 TON send_raw_message( begin_cell() .store_uint(0x18, 6) .store_slice(echo_address) .store_coins(0) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - .store_uint(1, 32) ;; let 1 be echo opcode in our contract - .store_slice(sender) ;; forward user address + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) + .store_uint(1, 32) // let 1 be echo opcode in our contract + .store_slice(sender) // forward user address .end_cell(), - 64 ;; send the remaining value of an incoming msg + 64 // send the remaining value of an incoming msg ); } - elseif (op == 1) { ;; echo - throw_unless(502, equal_slice_bits(sender, echo_address)); ;; only accept echoes from our echo-contract + elseif (op == 1) { // echo + throw_unless(502, equal_slice_bits(sender, echo_address)); // only accept echoes from our echo-contract slice user = in_msg_body~load_msg_addr(); - {- + /* at this point we have skipped 1+ blocks so let's just generate the random number - -} + */ randomize_lt(); - int x = rand(2); ;; generate a random number (either 0 or 1) - if (x == 1) { ;; user won + int x = rand(2); // generate a random number (either 0 or 1) + if (x == 1) { // user won send_raw_message( begin_cell() .store_uint(0x18, 6) .store_slice(user) - .store_coins(2000000000) ;; 2 TON - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_coins(2000000000) // 2 TON + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) .end_cell(), - 3 ;; ignore errors & pay fees separately + 3 // ignore errors & pay fees separately ); } } diff --git a/docs/develop/smart-contracts/learn/deployment.md b/docs/develop/smart-contracts/learn/deployment.md index 0aec57e5c8..4a4b20a4f3 100644 --- a/docs/develop/smart-contracts/learn/deployment.md +++ b/docs/develop/smart-contracts/learn/deployment.md @@ -2,7 +2,7 @@ Steps to deploy a smart contract on TON blockchain: -1. Compile func to fift. +1. Compile FunC to fift. 2. Wrap the bytecode to BoC. 3. Execute the message address destination(address of the new contract) 4. Top up balance for this address. diff --git a/docs/develop/smart-contracts/messages.md b/docs/develop/smart-contracts/messages.md index 14e09f2287..761145b0c6 100644 --- a/docs/develop/smart-contracts/messages.md +++ b/docs/develop/smart-contracts/messages.md @@ -66,29 +66,29 @@ If a message is sent from the smart contract, some of those fields will be rewri Straight-forward serialization of the message would be as follows: ```func var msg = begin_cell() - .store_uint(0, 1) ;; tag - .store_uint(1, 1) ;; ihr_disabled - .store_uint(1, 1) ;; allow bounces - .store_uint(0, 1) ;; not bounced itself + .store_uint(0, 1) // tag + .store_uint(1, 1) // ihr_disabled + .store_uint(1, 1) // allow bounces + .store_uint(0, 1) // not bounced itself .store_slice(source) .store_slice(destination) - ;; serialize CurrencyCollection (see below) + // serialize CurrencyCollection (see below) .store_coins(amount) .store_dict(extra_currencies) - .store_coins(0) ;; ihr_fee - .store_coins(fwd_value) ;; fwd_fee - .store_uint(cur_lt(), 64) ;; lt of transaction - .store_uint(now(), 32) ;; unixtime of transaction - .store_uint(0, 1) ;; no init-field flag (Maybe) - .store_uint(0, 1) ;; inplace message body flag (Either) + .store_coins(0) // ihr_fee + .store_coins(fwd_value) // fwd_fee + .store_uint(cur_lt(), 64) // lt of transaction + .store_uint(now(), 32) // unixtime of transaction + .store_uint(0, 1) // no init-field flag (Maybe) + .store_uint(0, 1) // inplace message body flag (Either) .store_slice(msg_body) .end_cell(); ``` However, instead of step-by-step serialization of all fields, usually developers use shortcuts. Thus, let's consider how messages can be sent from the smart contract using an example from [elector-code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/elector-code.fc#L153). ```func -() send_message_back(addr, ans_tag, query_id, body, grams, mode) impure inline_ref { - ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000 +() send_message_back(addr, ans_tag, query_id, body, grams, mode) inline_ref { + // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000 var msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) diff --git a/docs/develop/smart-contracts/sdk/javascript.mdx b/docs/develop/smart-contracts/sdk/javascript.mdx index af61acb484..9302f6b1f0 100644 --- a/docs/develop/smart-contracts/sdk/javascript.mdx +++ b/docs/develop/smart-contracts/sdk/javascript.mdx @@ -44,7 +44,7 @@ npm create ton@latest - [Blueprint using on DoraHacks stream](https://www.youtube.com/watch?v=5ROXVM-Fojo) - [Create a new project](https://github.com/ton-org/blueprint#create-a-new-project) - [Develop a new smart contract](https://github.com/ton-org/blueprint#develop-a-new-contract) -- [[YouTube] Func with Blueprint EN](https://www.youtube.com/watch?v=7omBDfSqGfA&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) ([RU version](https://youtube.com/playlist?list=PLyDBPwv9EPsA5vcUM2vzjQOomf264IdUZ)) +- [[YouTube] FunC with Blueprint EN](https://www.youtube.com/watch?v=7omBDfSqGfA&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) ([RU version](https://youtube.com/playlist?list=PLyDBPwv9EPsA5vcUM2vzjQOomf264IdUZ)) ## See Also diff --git a/docs/develop/smart-contracts/security/secure-programming.mdx b/docs/develop/smart-contracts/security/secure-programming.mdx index 7ee393b8a2..98d89c6b6b 100644 --- a/docs/develop/smart-contracts/security/secure-programming.mdx +++ b/docs/develop/smart-contracts/security/secure-programming.mdx @@ -56,9 +56,9 @@ TON cannot automatically calculate the gas. The complete execution of the transa A typical message handler in TON follows this approach: ```func -() handle_something(...) impure { +() handle_something(...) { (int total_supply, ) = load_data(); - ... ;; do something, change data + ... // do something, change data save_data(total_supply, ); } ``` @@ -121,8 +121,8 @@ Many contracts also process bounced messages just in case. For example, in TON Jetton, if the recipient's wallet cannot accept any tokens (it depends on the logic for receiving), then the sender's wallet will process the bounced message and return the tokens to its own balance. ```func -() on_bounce (slice in_msg_body) impure { - in_msg_body~skip_bits(32); ;;0xFFFFFFFF +() on_bounce (slice in_msg_body) { + in_msg_body~skip_bits(32); //0xFFFFFFFF (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data(); @@ -181,13 +181,13 @@ If something does not meet the expectations of your contract, the Jettons must b We found an example of this vulnerable implementation in a recent audit. ```func -() handle_transfer_notification(...) impure { +() handle_transfer_notification(...) { ... int jetton_amount = in_msg_body~load_coins(); slice from_address = in_msg_body~load_msg_addr(); slice from_jetton_address = in_msg_body~load_msg_addr(); - if (msg_value < gas_consumption) { ;; not enough gas provided + if (msg_value < gas_consumption) { // not enough gas provided if (equal_slices(from_jetton_address, jettonA_address)) { var msg = begin_cell() .store_uint(0x18, 6) @@ -223,13 +223,13 @@ TON has a useful mechanism: `SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE = 64`. 1. If there are no other non-linear handlers in your contract. storage_fee is deducted from the balance of the contract, and not from the incoming gas. This means that over time, storage_fee can eat up the entire balance because everything that comes in has to go out; 1. If your contract emits events, i.e. sends a message to an external address. The cost of this action is deducted from the balance of the contract, and not from msg_value. ```func -() emit_log_simple (int event_id, int query_id) impure inline { +() emit_log_simple (int event_id, int query_id) inline { var msg = begin_cell() - .store_uint (12, 4) ;; ext_out_msg_info$11 addr$00 - .store_uint (1, 2) ;; addr_extern$01 - .store_uint (256, 9) ;; len:(## 9) - .store_uint(event_id, 256); ;; external_address:(bits len) - .store_uint(0, 64 + 32 + 1 + 1) ;; lt, at, init, body + .store_uint (12, 4) // ext_out_msg_info$11 addr$00 + .store_uint (1, 2) // addr_extern$01 + .store_uint (256, 9) // len:(## 9) + .store_uint(event_id, 256); // external_address:(bits len) + .store_uint(0, 64 + 32 + 1 + 1) // lt, at, init, body .store_query_id(query_id) .end_cell(); @@ -250,7 +250,7 @@ if(forward_ton_amount) { ... } -if (msg_value > 0) { ;; there is still something to return +if (msg_value > 0) { // there is still something to return var msg = begin_cell() .store_uint(0x10, 6) @@ -267,7 +267,7 @@ Remember, if the value of the contract balance runs out, the transaction will be We recommend the following storage organization approach: ```func -() handle_something(...) impure { +() handle_something(...) { (slice swap_data, cell liquidity_data, cell mining_data, cell discovery_data) = load_data(); (int total_supply, int swap_fee, int min_amount, int is_stopped) = swap_data.parse_swap_data(); … @@ -293,14 +293,14 @@ global int var1; global cell var2; global slice var3; -() load_data() impure { +() load_data() { var cs = get_data().begin_parse(); var1 = cs~load_coins(); var2 = cs~load_ref(); var3 = cs~load_bits(512); } -() save_data() impure { +() save_data() { set_data( begin_cell() .store_coins(var1) @@ -328,9 +328,9 @@ FunC initially has an incredible amount of magic numbers. If the developer does ```func var msg = begin_cell() - .store_uint(0xc4ff, 17) ;; 0 11000100 0xff + .store_uint(0xc4ff, 17) // 0 11000100 0xff .store_uint(config_addr, 256) - .store_grams(1 << 30) ;; ~1 gram of value + .store_grams(1 << 30) // ~1 gram of value .store_uint(0, 107) .store_uint(0x4e565354, 32) .store_uint(query_id, 64) @@ -357,7 +357,7 @@ builder store_body_header(builder b, int op, int query_id) inline { return b.store_uint(op, 32).store_uint(query_id, 64); } -() mint_tokens(slice to_address, cell jetton_wallet_code, int amount, cell master_msg) impure { +() mint_tokens(slice to_address, cell jetton_wallet_code, int amount, cell master_msg) { cell state_init = calculate_jetton_wallet_state_init(to_address, my_address(), jetton_wallet_code); slice to_wallet_address = calculate_address_by_state_init(state_init); diff --git a/docs/develop/smart-contracts/security/things-to-focus.md b/docs/develop/smart-contracts/security/things-to-focus.md index 4f6364ba3e..85d689adf1 100644 --- a/docs/develop/smart-contracts/security/things-to-focus.md +++ b/docs/develop/smart-contracts/security/things-to-focus.md @@ -6,9 +6,9 @@ In this article, we will review and discuss the elements to consider for those w ### 1. Name collisions -Func variables and functions may contain almost any legit character. I.e. `var++`, `~bits`, `foo-bar+baz` including commas are valid variables and functions names. +FunC variables and functions may contain almost any legit character. I.e. `var++`, `~bits`, `foo-bar+baz` including commas are valid variables and functions names. -When writing and inspecting a Func code, Linter should be used. +When writing and inspecting a FunC code, Linter should be used. - [IDE plugins](/develop/smart-contracts/environment/ide-plugins/) @@ -16,10 +16,10 @@ When writing and inspecting a Func code, Linter should be used. Each time the TVM execution stops normally, it stops with exit codes `0` or `1`. Although it is done automatically, TVM execution can be interrupted directly in an unexpected way if exit codes `0` and `1` are thrown directly by either `throw(0)` or `throw(1)` command. -- [How to handle errors](/develop/func/builtins#throwing-exceptions) +- [How to handle errors](/develop/func/stdlib#throwing-exceptions) - [TVM exit codes](/learn/tvm-instructions/tvm-exit-codes) -### 3. Func is a strictly typed language with data structures holding exactly what they are supposed to store +### 3. FunC is a strictly typed language with data structures holding exactly what they are supposed to store It is crucial to keep track of what the code does and what it may return. Keep in mind that the compiler cares only about the code and only in its initial state. After certain operations stored values of some variables can change. @@ -50,9 +50,9 @@ Contracts in the blockchain can reside in separate shards, processed by other se It is helpful to think through the roadmap of exit codes for the code flow (and have it documented) before starting programming your TON smart contract. -### 9. Func functions that have medhod_id identifiers have method IDs +### 9. FunC functions declared as `get` have method ID -They can be either set explicitly `"method_id(5)"`, or implicitly by a func compiler. In this case, they can be found among methods declarations in the .fift assembly file. Two of them are predefined: one for receiving messages inside of blockchain `(0)`, commonly named `recv_internal`, and one for receiving messages from outside `(-1)`, `recv_external`. +For get methods, FunC calculated an ID that is saved into Fift bytecode (such functions can be called by name, but actually, a client calculated the same hash). Two IDs are predefined: one for receiving messages inside of blockchain `(0)`, commonly named `recv_internal`, and one for receiving messages from outside `(-1)`, `recv_external`. ### 10. TON Crypto address may not have any coins or code @@ -67,7 +67,7 @@ A full representation can either be "raw" (`workchain:address`) or "user-friendl ### 12. Keep track of the flaws in code execution -Unlike Solidity where it's up to you to set methods visibility, in the case of Func, the visibility is restricted in a more intricate way either by showing errors or by `if` statements. +Unlike Solidity where it's up to you to set methods visibility, in the case of FunC, the visibility is restricted in a more intricate way either by showing errors or by `if` statements. ### 13. Keep an eye on gas before sending bounced messages @@ -94,4 +94,4 @@ There are two custom solutions for wallets (smart contracts, storing users money Originally written by 0xguard -- [Original article](https://0xguard.com/things_to_focus_on_while_working_with_ton_blockchain) \ No newline at end of file +- [Original article](https://0xguard.com/things_to_focus_on_while_working_with_ton_blockchain) diff --git a/docs/develop/smart-contracts/security/ton-hack-challenge-1.md b/docs/develop/smart-contracts/security/ton-hack-challenge-1.md index a4c1714290..e5162b0c70 100644 --- a/docs/develop/smart-contracts/security/ton-hack-challenge-1.md +++ b/docs/develop/smart-contracts/security/ton-hack-challenge-1.md @@ -9,17 +9,12 @@ Source code and contest rules were hosted on GitHub [here](https://github.com/to ### 1. Mutual fund -:::note SECURITY RULE -Always check functions for [`impure`](/develop/func/functions#impure-specifier) modifier. +:::note Doesn't make sense starting from FunC v0.5.0. +Before v0.5.0, there was an `impure` specifier. If it was absent, FunC could delete a function call. +The attack was able because an author forgot `impure`. ::: -The first task was very simple. The attacker could find that `authorize` function was not `impure`. The absence of this modifier allows a compiler to skip calls to that function if it returns nothing or the return value is unused. - -```func -() authorize (sender) inline { - throw_unless(187, equal_slice_bits(sender, addr1) | equal_slice_bits(sender, addr2)); -} -``` +Now all functions are impure by default, because old behavior was too unexpected. ### 2. Bank @@ -42,15 +37,15 @@ Use signed integers if you really need it. Voting power was stored in message as an integer. So the attacker could send a negative value during power transfer and get infinite voting power. ```func -(cell,()) transfer_voting_power (cell votes, slice from, slice to, int amount) impure { +(cell,()) transfer_voting_power (cell votes, slice from, slice to, int amount) { int from_votes = get_voting_power(votes, from); int to_votes = get_voting_power(votes, to); from_votes -= amount; to_votes += amount; - ;; No need to check that result from_votes is positive: set_voting_power will throw for negative votes - ;; throw_unless(998, from_votes > 0); + // No need to check that result from_votes is positive: set_voting_power will throw for negative votes + // throw_unless(998, from_votes > 0); votes~set_voting_power(from, from_votes); votes~set_voting_power(to, to_votes); @@ -76,7 +71,7 @@ if(in_msg_body.slice_bits() > 0) { set_seed(seed); var balance = get_balance().pair_first(); if(balance > 5000 * 1000000000) { - ;; forbid too large jackpot + // forbid too large jackpot raw_reserve( balance - 5000 * 1000000000, 0); } if(rand(10000) == 7777) { ...send reward... } @@ -103,11 +98,11 @@ The vault has the following code in the database message handler: ```func int mode = null(); if (op == op_not_winner) { - mode = 64; ;; Refund remaining check-TONs - ;; addr_hash corresponds to check requester + mode = 64; // Refund remaining check-TONs + // addr_hash corresponds to check requester } else { - mode = 128; ;; Award the prize - ;; addr_hash corresponds to the withdrawal address from the winning entry + mode = 128; // Award the prize + // addr_hash corresponds to the withdrawal address from the winning entry } ``` @@ -138,9 +133,9 @@ slice safe_execute(int image, (int -> slice) dehasher) inline { slice preimage = try_execute(image, dehasher); - ;; restore c4 if dehasher spoiled it + // restore c4 if dehasher spoiled it set_data(c4); - ;; clean actions if dehasher spoiled them + // clean actions if dehasher spoiled them set_c5(begin_cell().end_cell()); return preimage; diff --git a/docs/develop/smart-contracts/testing/toncli.md b/docs/develop/smart-contracts/testing/toncli.md index cad445bc24..e420b05ab8 100644 --- a/docs/develop/smart-contracts/testing/toncli.md +++ b/docs/develop/smart-contracts/testing/toncli.md @@ -70,7 +70,7 @@ int __test_example() { Since we will need to frequently update the `c4` register due to a large number of tests, so we will create a helper function that will write `c4` to zero ```js -() set_default_initial_data() impure { +() set_default_initial_data() { set_data(begin_cell().store_uint(0, 64).end_cell()); } ``` @@ -80,8 +80,6 @@ Since we will need to frequently update the `c4` register due to a large number * `end_cell()`- create Cell * `set_data()` - writes the cell to register c4 -`impure` is a keyword that indicates that the function changes the smart contract data. - We got a function that we will use in the body of our testing function **Result:** @@ -89,7 +87,6 @@ We got a function that we will use in the body of our testing function ```js int __test_example() { set_default_initial_data(); - } ``` diff --git a/docs/develop/smart-contracts/tutorials/multisig.md b/docs/develop/smart-contracts/tutorials/multisig.md index d96144af2a..bda7bc5643 100644 --- a/docs/develop/smart-contracts/tutorials/multisig.md +++ b/docs/develop/smart-contracts/tutorials/multisig.md @@ -29,7 +29,7 @@ Before we begin our journey, check and prepare your environment. - Install `func`, `fift`, `lite-client` binaries and `fiftlib` from the [Installation](/develop/smart-contracts/environment/installation) section. - Clone [repository](https://github.com/akifoq/multisig) and open its directory in CLI. -```cpp +``` https://github.com/akifoq/multisig.git cd ~/multisig ``` @@ -47,7 +47,7 @@ cd ~/multisig Compile the contract to Fift with: -```cpp +```bash func -o multisig-code.fif -SPA stdlib.fc multisig-code.fc ``` @@ -57,7 +57,7 @@ func -o multisig-code.fif -SPA stdlib.fc multisig-code.fc To create a key you need to run: -```cpp +```bash fift -s new-key.fif $KEY_NAME$ ``` @@ -65,7 +65,7 @@ fift -s new-key.fif $KEY_NAME$ For example: -```cpp +```bash fift -s new-key.fif multisig_key ``` @@ -98,7 +98,7 @@ PubH821csswh8R1uO9rLYyP1laCpYWxhNkx+epOkqwdWXgzY4 After that, you need to run: -```cpp +```bash fift -s new-multisig.fif 0 $WALLET_ID$ wallet $KEYS_COUNT$ ./keys.txt ``` @@ -168,7 +168,7 @@ After that, the wallet will be ready to work within a minute. First you need to create a message request: -```cpp +```bash fift -s create-msg.fif $ADDRESS$ $AMOUNT$ $MESSAGE$ ``` @@ -178,7 +178,7 @@ fift -s create-msg.fif $ADDRESS$ $AMOUNT$ $MESSAGE$ For example: -```cpp +```bash fift -s create-msg.fif EQApAj3rEnJJSxEjEHVKrH3QZgto_MQMOmk8l72azaXlY1zB 0.1 message ``` diff --git a/docs/develop/smart-contracts/tutorials/wallet.md b/docs/develop/smart-contracts/tutorials/wallet.md index 9ef00f179f..9d614136f6 100644 --- a/docs/develop/smart-contracts/tutorials/wallet.md +++ b/docs/develop/smart-contracts/tutorials/wallet.md @@ -168,21 +168,21 @@ On TON wallet smart contracts help the platform communicate with other smart con Generally, there are two transaction types on TON Blockchain: `internal` and `external`. External transactions allow for the ability to send messages to the blockchain from the outside world, thus allowing for the communication with smart contracts that accept such transactions. The function responsible for carrying out this process is as follows: ```func -() recv_external(slice in_msg) impure { - ;; some code +() recv_external(slice in_msg) { + // some code } ``` Before we dive into more details concerning wallets, let’s look at how wallets accept external transactions. On TON, all wallets hold the owner’s `public key`, `seqno`, and `subwallet_id`. When receiving an external transaction, the wallet uses the `get_data()` method to retrieve data from the storage portion of the wallet. It then conducts several verification procedures and decides whether to accept the transaction or not. This process is conducted as follows: ```func -() recv_external(slice in_msg) impure { - var signature = in_msg~load_bits(512); ;; get signature from the message body +() recv_external(slice in_msg) { + var signature = in_msg~load_bits(512); // get signature from the message body var cs = in_msg; - var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); ;; get rest values from the message body - throw_if(35, valid_until <= now()); ;; check the relevance of the transaction - var ds = get_data().begin_parse(); ;; get data from storage and convert it into a slice to be able to read values - var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); ;; read values from storage - ds.end_parse(); ;; make sure we do not have anything in ds variable + var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); // get rest values from the message body + throw_if(35, valid_until <= now()); // check the relevance of the transaction + var ds = get_data().begin_parse(); // get data from storage and convert it into a slice to be able to read values + var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); // read values from storage + ds.end_parse(); // make sure we do not have anything in ds variable throw_unless(33, msg_seqno == stored_seqno); throw_unless(34, subwallet_id == stored_subwallet); throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); @@ -273,8 +273,8 @@ As we discussed earlier, a wallet smart contract accepts external transactions, ```func cs~touch(); while (cs.slice_refs()) { - var mode = cs~load_uint(8); ;; load transaction mode - send_raw_message(cs~load_ref(), mode); ;; get each new internal message as a cell with the help of load_ref() and send it + var mode = cs~load_uint(8); // load transaction mode + send_raw_message(cs~load_ref(), mode); // get each new internal message as a cell with the help of load_ref() and send it } ``` @@ -339,26 +339,26 @@ Internal transactions are used to send messages between contracts. When analyzin ```func var msg = begin_cell() - .store_uint(0x18, 6) ;; or 0x10 for non-bounce + .store_uint(0x18, 6) // or 0x10 for non-bounce .store_slice(to_address) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - ;; store something as a body + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) + // store something as a body ``` Let’s first consider `0x18` and `0x10` (x - hexadecimal), which are hexadecimal numbers laid out in the following manner (given that we allocate 6 bits): `011000` and `010000`. This means that the code above can be overwritten as follows: ```func var msg = begin_cell() - .store_uint(0, 1) ;; this bit indicates that we send an internal message according to int_msg_info$0 - .store_uint(1, 1) ;; IHR Disabled - .store_uint(1, 1) ;; or .store_uint(0, 1) for 0x10 | bounce - .store_uint(0, 1) ;; bounced - .store_uint(0, 2) ;; src -> two zero bits for addr_none + .store_uint(0, 1) // this bit indicates that we send an internal message according to int_msg_info$0 + .store_uint(1, 1) // IHR Disabled + .store_uint(1, 1) // or .store_uint(0, 1) for 0x10 | bounce + .store_uint(0, 1) // bounced + .store_uint(0, 2) // src -> two zero bits for addr_none .store_slice(to_address) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - ;; store something as a body + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) + // store something as a body ``` Now let’s go through each option in detail: @@ -382,14 +382,14 @@ Finally, let’s look at the remaining lines of code: ```func ... - .store_uint(0, 1) ;; Extra currency - .store_uint(0, 4) ;; IHR fee - .store_uint(0, 4) ;; Forwarding fee - .store_uint(0, 64) ;; Logical time of creation - .store_uint(0, 32) ;; UNIX time of creation - .store_uint(0, 1) ;; State Init - .store_uint(0, 1) ;; Message body - ;; store something as a body + .store_uint(0, 1) // Extra currency + .store_uint(0, 4) // IHR fee + .store_uint(0, 4) // Forwarding fee + .store_uint(0, 64) // Logical time of creation + .store_uint(0, 32) // UNIX time of creation + .store_uint(0, 1) // State Init + .store_uint(0, 1) // Message body + // store something as a body ``` Option | Explanation :---: | :---: @@ -856,7 +856,7 @@ Now we have the following structure for the project we are creating: ``` :::info -It’s fine if your IDE plugin conflicts with the `() set_seed(int) impure asm "SETRAND";` in the `stdlib.fc` file. +It’s fine if your IDE plugin conflicts with the `() set_seed(int) asm "SETRAND";` in the `stdlib.fc` file. ::: Remember to add the following line to the beginning of the `wallet_v3.fc` file to indicate that the functions from the stdlib will be used below: @@ -2109,21 +2109,21 @@ Exchanges are probably the best example of where high-load wallets are used on a First, let’s examine [the code structure of high-load wallet smart contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/highload-wallet-v2-code.fc): ```func -() recv_external(slice in_msg) impure { - var signature = in_msg~load_bits(512); ;; get signature from the message body +() recv_external(slice in_msg) { + var signature = in_msg~load_bits(512); // get signature from the message body var cs = in_msg; - var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); ;; get rest values from the message body - var bound = (now() << 32); ;; bitwise left shift operation - throw_if(35, query_id < bound); ;; throw an error if transaction has expired + var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); // get rest values from the message body + var bound = (now() << 32); // bitwise left shift operation + throw_if(35, query_id < bound); // throw an error if transaction has expired var ds = get_data().begin_parse(); - var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); ;; read values from storage - ds.end_parse(); ;; make sure we do not have anything in ds - (_, var found?) = old_queries.udict_get?(64, query_id); ;; check if we have already had such a request - throw_if(32, found?); ;; if yes throw an error + var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); // read values from storage + ds.end_parse(); // make sure we do not have anything in ds + (_, var found?) = old_queries.udict_get?(64, query_id); // check if we have already had such a request + throw_if(32, found?); // if yes throw an error throw_unless(34, subwallet_id == stored_subwallet); throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); - var dict = cs~load_dict(); ;; get dictionary with messages - cs.end_parse(); ;; make sure we do not have anything in cs + var dict = cs~load_dict(); // get dictionary with messages + cs.end_parse(); // make sure we do not have anything in cs accept_message(); ``` @@ -2145,10 +2145,10 @@ This process takes a significant amount of time which high-load wallets are not If the same transaction request already exists, the contract won’t accept it, as it has already been processed: ```func -var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); ;; read values from storage -ds.end_parse(); ;; make sure we do not have anything in ds -(_, var found?) = old_queries.udict_get?(64, query_id); ;; check if we have already had such a request -throw_if(32, found?); ;; if yes throw an error +var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); // read values from storage +ds.end_parse(); // make sure we do not have anything in ds +(_, var found?) = old_queries.udict_get?(64, query_id); // check if we have already had such a request +throw_if(32, found?); // if yes throw an error ``` This way, we are **being protected from repeat transactions**, which was the role of seqno in ordinary wallets. @@ -2158,14 +2158,14 @@ This way, we are **being protected from repeat transactions**, which was the rol After the contract has accepted the external message, a loop starts, in which the `slices` stored in the dictionary are taken. These slices store transaction modes and the transactions themselves. Sending new transactions takes place until the dictionary is empty. ```func -int i = -1; ;; we write -1 because it will be the smallest value among all dictionary keys +int i = -1; // we write -1 because it will be the smallest value among all dictionary keys do { - (i, var cs, var f) = dict.idict_get_next?(16, i); ;; get the key and its corresponding value with the smallest key, which is greater than i - if (f) { ;; check if any value was found - var mode = cs~load_uint(8); ;; load transaction mode - send_raw_message(cs~load_ref(), mode); ;; load transaction itself and send it + (i, var cs, var f) = dict.idict_get_next?(16, i); // get the key and its corresponding value with the smallest key, which is greater than i + if (f) { // check if any value was found + var mode = cs~load_uint(8); // load transaction mode + send_raw_message(cs~load_ref(), mode); // load transaction itself and send it } -} until (~ f); ;; if any value was found continue +} until (~ f); // if any value was found continue ``` > 💡 Useful link: @@ -2180,18 +2180,18 @@ Typically, [smart contracts on TON pay for their own storage](/develop/howto/fee ```func -bound -= (64 << 32); ;; clean up records that have expired more than 64 seconds ago -old_queries~udict_set_builder(64, query_id, begin_cell()); ;; add current query to dictionary -var queries = old_queries; ;; copy dictionary to another variable +bound -= (64 << 32); // clean up records that have expired more than 64 seconds ago +old_queries~udict_set_builder(64, query_id, begin_cell()); // add current query to dictionary +var queries = old_queries; // copy dictionary to another variable do { var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64); f~touch(); - if (f) { ;; check if any value was found - f = (i < bound); ;; check if more than 64 seconds have elapsed after expiration + if (f) { // check if any value was found + f = (i < bound); // check if more than 64 seconds have elapsed after expiration } if (f) { - old_queries = old_queries'; ;; if yes save changes in our dictionary - last_cleaned = i; ;; save last removed query + old_queries = old_queries'; // if yes save changes in our dictionary + last_cleaned = i; // save last removed query } } until (~ f); ``` @@ -2207,7 +2207,7 @@ Note that it is necessary to interact with the `f` variable several times. Since This section may seem a bit complicated for those who have not previously worked with bitwise operations. The following line of code can be seen in the smart contract code: ```func -var bound = (now() << 32); ;; bitwise left shift operation +var bound = (now() << 32); // bitwise left shift operation ``` As a result 32 bits are added to the number on the right side. This means that **existing values are moved 32 bits to the left**. For example, let’s consider the number 3 and translate it into a binary form with a result of 11. Applying the `3 << 2` operation, 11 is moved 2 bit places. This means that two bits are added to the right of the string. In the end, we have 1100, which is 12. @@ -2216,14 +2216,14 @@ The first thing to understand about this process is to remember that the `now()` Next, let’s consider the following line of code: ```func -bound -= (64 << 32); ;; clean up the records that have expired more than 64 seconds ago +bound -= (64 << 32); // clean up the records that have expired more than 64 seconds ago ``` Above we performed an operation to shift the number 64 by 32 bits to **subtract 64 seconds** from our timestamp. This way we'll be able to compare past query_ids and see if they are less than the received value. If so, they expired more than 64 seconds ago: ```func -if (f) { ;; check if any value has been found - f = (i < bound); ;; check if more than 64 seconds have elapsed after expiration +if (f) { // check if any value has been found + f = (i < bound); // check if more than 64 seconds have elapsed after expiration } ``` To understand this better, let’s use the number `1625918400` as an example of a timestamp. Its binary representation (with the left-handed addition of zeros for 32 bits) is 01100000111010011000101111000000. By performing a 32 bit bitwise left shift, the result is 32 zeros at the end of the binary representation of our number. @@ -2256,7 +2256,7 @@ int get_public_key() | Rerive a public key. We have considered this method befor Let’s look at the `int processed?(int query_id)` method closely to help us to understand why we need to make use of the last_cleaned: ```func -int processed?(int query_id) method_id { +get int processed?(int query_id) { var ds = get_data().begin_parse(); var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); ds.end_parse(); diff --git a/docs/learn/tvm-instructions/tvm-exit-codes.md b/docs/learn/tvm-instructions/tvm-exit-codes.md index a54863b6fc..b7c9ffd97a 100644 --- a/docs/learn/tvm-instructions/tvm-exit-codes.md +++ b/docs/learn/tvm-instructions/tvm-exit-codes.md @@ -21,7 +21,7 @@ The list of standard exit codes contains all universal TVM exit codes defined fo | `8` | Compute Phase | Cell overflow. Writing to builder is not possible since after operation there would be more than 1023 bits or 4 references. | | `9` | Compute Phase | Cell underflow. Read from slice primitive tried to read more bits or references than there are. | | `10` | Compute Phase | Dictionary error. Error during manipulation with dictionary (hashmaps). | -| `11` | Compute Phase | Most often caused by trying to call get-method whose id wasn't found in the code (missing `method_id` modifier or wrong get-method name specified when trying to call it). In [TVM docs](https://ton.org/tvm.pdf) its described as "Unknown error, may be thrown by user programs". | +| `11` | Compute Phase | Most often caused by trying to call get-method which wasn't found in the code (missing `get` modifier or wrong name specified when trying to call it). In [TVM docs](https://ton.org/tvm.pdf) it's described as "Unknown error, may be thrown by user programs". | | `12` | Compute Phase | Thrown by TVM in situations deemed impossible. | | `13` | Compute Phase | Out of gas error. Thrown by TVM when the remaining gas becomes negative. | | `-14` | Compute Phase | It means out of gas error, same as `13`. Negative, because it [cannot be faked](https://github.com/ton-blockchain/ton/blob/20758d6bdd0c1327091287e8a620f660d1a9f4da/crypto/vm/vm.cpp#L492) | @@ -36,8 +36,8 @@ The list of standard exit codes contains all universal TVM exit codes defined fo | `40` | Action Phase | Not enough funds to process a message. This error is thrown when there is only enough gas to cover part of the message, but does not cover it completely. | | `43` | Action Phase | The maximum number of cells in the library is exceeded or the maximum depth of the Merkle tree is exceeded. | -1 If you encounter such exception in a func contract it probably means a type error in asm declarations. +1 If you encounter such exception in a FunC contract it probably means a type error in asm declarations. :::info Often you can see the exit code `0xffff` (65535 in decimal form). This usually means that the received opcode is unknown to the contract. When writing contracts, this code is set by the developer himself. -::: \ No newline at end of file +::: diff --git a/docs/learn/tvm-instructions/tvm-upgrade-2023-07.md b/docs/learn/tvm-instructions/tvm-upgrade-2023-07.md index 57273f4828..e02735fe62 100644 --- a/docs/learn/tvm-instructions/tvm-upgrade-2023-07.md +++ b/docs/learn/tvm-instructions/tvm-upgrade-2023-07.md @@ -24,7 +24,7 @@ Using **10** for code is compatible to Everscale update of tvm. **11** Currently value of the incoming message is presented on stack after TVM initiation, so if needed during execution, one either need to store it to global variable or pass through local variables -(at funC level it looks like additional `msg_value` argument in all functions). +(at FunC level it looks like additional `msg_value` argument in all functions). By putting it to **11** element we will repeat behavior of contract balance: it is presented both on stack and in c7. **12** Currently the only way to calculate storage fees is to store balance in the previous transaction, diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/contribute/tutorials/sample-tutorial.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/contribute/tutorials/sample-tutorial.md index 472f351163..a0aba4ff4c 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/contribute/tutorials/sample-tutorial.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/contribute/tutorials/sample-tutorial.md @@ -63,7 +63,7 @@ - \```javascript *或* ```js可用于任何JavaScript代码。 - \```typescript或```ts可用于任何TypeScript代码。 - \```jsx用于ReactJS代码。 - - \```cpp用于Func代码。 + - \```func用于FunC代码。 - 使用\```graphql突出显示GraphQL语法。 - 使用\```json突出显示有效的JSON。(对于无效的JSON示例,请使用\```text。) - \```bash应*仅*用于需要#样式注释的代码块。这必须小心进行,因为在许多情况下,#字符将呈现为markdown标题。如果发生这种情况,通常目录会受到影响。 diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/nfts.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/nfts.md index 4bcdb70382..f2c02fc404 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/nfts.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/nfts.md @@ -17,7 +17,7 @@ Open Network (TON) 区块链设计考虑了高性能,并包括了一个功能 ### NFT 集合 NFT 集合是一个用于索引和存储 NFT 内容的合约,并应包含以下接口: #### 获取方法 `get_collection_data` -``` +```func (int next_item_index, cell collection_content, slice owner_address) get_collection_data() ``` 获取关于集合的一般信息,表示如下: @@ -26,13 +26,13 @@ NFT 集合是一个用于索引和存储 NFT 内容的合约,并应包含以 3. `owner_address` - 包含集合所有者地址的 slice(此值也可以为空)。 #### 获取方法 `get_nft_address_by_index` -``` +```func (slice nft_address) get_nft_address_by_index(int index) ``` 此方法可用于验证 NFT 的真实性,并确认它是否确实属于特定集合。它还使用户能够通过提供其在集合中的索引来检索 NFT 地址。该方法应返回包含与提供的索引对应的 NFT 地址的 slice。 #### 获取方法 `get_nft_content` -``` +```func (cell full_content) get_nft_content(int index, cell individual_content) ``` 由于集合充当 NFT 的公共数据存储,因此需要此方法来完善 NFT 内容。要使用此方法,首先需要通过调用相应的 `get_nft_data()` 方法获取 NFT 的 `individual_content`。获取 `individual_content` 后,可以使用 NFT 索引和 `individual_content` cell 调用 `get_nft_content()` 方法。该方法应返回一个包含 NFT 全部内容的 TEP-64 cell。 @@ -41,12 +41,12 @@ NFT 集合是一个用于索引和存储 NFT 内容的合约,并应包含以 基本 NFT 应实现: #### 获取方法 `get_nft_data()` -``` +```func (int init?, int index, slice collection_address, slice owner_address, cell individual_content) get_nft_data() ``` #### 内联消息处理器 `transfer` -``` +```tlb transfer#5fcc3d14 query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress custom_payload:(Maybe ^Cell) forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) = InternalMsgBody ``` 让我们看一下您需要在消息中填充的每个参数: @@ -179,19 +179,19 @@ curl -X 'POST' \ 在这个例子中,NFT 转移信息位于 [第 67 行](https://www.google.com/url?q=https://github.com/ton-blockchain/token-contract/blob/1ad314a98d20b41241d5329e1786fc894ad811de/nft/nft-sale.fc%23L67&sa=D&source=docs&ust=1685436161341866&usg=AOvVaw1yuoIzcbEuvqMS4xQMqfXE): -``` +```func var nft_msg = begin_cell() .store_uint(0x18, 6) .store_slice(nft_address) .store_coins(0) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息头(见发送消息页面) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息头(见发送消息页面) .store_uint(op::transfer(), 32) .store_uint(query_id, 64) - .store_slice(sender_address) ;; new_owner_address - .store_slice(sender_address) ;; response_address - .store_int(0, 1) ;; 空的自定义有效载荷 - .store_coins(0) ;; 向 new_owner_address 转发金额 - .store_int(0, 1); ;; 空的转发有效载荷 + .store_slice(sender_address) // new_owner_address + .store_slice(sender_address) // response_address + .store_int(0, 1) // 空的自定义有效载荷 + .store_coins(0) // 向 new_owner_address 转发金额 + .store_int(0, 1); // 空的转发有效载荷 send_raw_message(nft_msg.end_cell(), 128 + 32); @@ -203,8 +203,8 @@ send_raw_message(nft_msg.end_cell(), 128 + 32); - `store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)` - 剩余构成消息头的部分被留空。 - `store_uint(op::transfer(), 32)` - 这是 msg_body 的开始。在这里,我们首先使用 transfer OP 代码,以便接收者理解其转移所有权消息。 - `store_uint(query_id, 64)` - 存储查询 ID。 -- `store_slice(sender_address) ;; new_owner_address` - 第一个存储的地址是用于转移 NFTs 和发送通知的地址。 -- `store_slice(sender_address) ;; response_address` - 第二个存储的地址是响应地址。 +- `store_slice(sender_address) // new_owner_address` - 第一个存储的地址是用于转移 NFTs 和发送通知的地址。 +- `store_slice(sender_address) // response_address` - 第二个存储的地址是响应地址。 - `store_int(0, 1)` - 自定义有效载荷标志设置为 0,表示不需要自定义有效载荷。 - `store_coins(0)` - 随消息转发的 TON 数量。在这个例子中设置为 0,但是,建议将此值设置为更高的金额(如至少 0.01 TON),以便创建转发消息并通知新所有者他们已经收到了 NFT。金额应足以覆盖任何相关费用和成本。 - `.store_int(0, 1)` - 自定义有效载荷标志。如果您的服务应该作为 ref 传递有效载荷,则必须将其设置为 `1`。 @@ -212,11 +212,11 @@ send_raw_message(nft_msg.end_cell(), 128 + 32); ### 接收 NFTs 一旦我们发送了 NFT,就至关重要的是确定新所有者何时收到了它。一个好的例子可以在同一个 NFT 销售智能合约中找到: -``` +```func slice cs = in_msg_full.begin_parse(); int flags = cs~load_uint(4); -if (flags & 1) { ;; 忽略所有弹回消息 +if (flags & 1) { // 忽略所有弹回消息 return (); } slice sender_address = cs~load_msg_addr(); @@ -230,11 +230,11 @@ slice prev_owner_address = in_msg_body~load_msg_addr(); - `slice cs = in_msg_full.begin_parse();` - 用于解析传入消息。 - `int flags = cs~load_uint(4);` - 用于从消息的前 4 位加载标志。 -- `if (flags & 1) { return (); } ;; 忽略所有弹回消息` - 用于验证消息是否没有被弹回。对于所有您的传入消息,如果没有理由反之,就很重要进行此过程。弹回的消息是那些在尝试接收交易时遇到错误并被退回给发件人的消息。 +- `if (flags & 1) { return (); } // 忽略所有弹回消息` - 用于验证消息是否没有被弹回。对于所有您的传入消息,如果没有理由反之,就很重要进行此过程。弹回的消息是那些在尝试接收交易时遇到错误并被退回给发件人的消息。 - `slice sender_address = cs~load_msg_addr();` - 接下来加载消息发送者。在这种特殊情况下,通过使用 NFT 地址完成。 - `throw_unless(500, equal_slices(sender_address, nft_address));` - 用于验证发送者确实是应该通过合约转移的 NFT。从智能合约解析 NFT 数据相当困难,因此在大多数情况下,NFT 地址在合约创建时预定义。 - `int op = in_msg_body~load_uint(32);` - 加载消息 OP 代码。 - `throw_unless(501, op == op::ownership_assigned());` - 确保接收的 OP 代码与所有权分配的常量值匹配。 - `slice prev_owner_address = in_msg_body~load_msg_addr();` - 从传入消息体中提取的前所有者地址,并加载到 `prev_owner_address` 切片变量中。如果前所有者选择取消合约并将 NFT 归还给他们,这一点可能很有用。 -现在我们已经成功地解析并验证了通知消息,我们可以继续我们的业务逻辑,这用于启动销售智能合约(它旨在处理 NFT 物品业务销售过程,例如 NFT 拍卖,如 getgems.io) \ No newline at end of file +现在我们已经成功地解析并验证了通知消息,我们可以继续我们的业务逻辑,这用于启动销售智能合约(它旨在处理 NFT 物品业务销售过程,例如 NFT 拍卖,如 getgems.io) diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/simple-zk-on-ton.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/simple-zk-on-ton.md index 537a7b0191..1d325e513e 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/simple-zk-on-ton.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/simple-zk-on-ton.md @@ -243,7 +243,7 @@ int bls_pairing(slice x1, slice y1, slice x2, slice y2, slice x3, slice y3, slic load_data 和 save_data 函数仅用于加载和保存证明验证结果(仅用于测试目的)。 ```func -() load_data() impure { +() load_data() { var ds = get_data().begin_parse(); @@ -252,7 +252,7 @@ load_data 和 save_data 函数仅用于加载和保存证明验证结果(仅 ds.end_parse(); } -() save_data() impure { +() save_data() { set_data( begin_cell() .store_uint(ctx_res, 32) @@ -263,15 +263,15 @@ load_data 和 save_data 函数仅用于加载和保存证明验证结果(仅 接下来,有几个简单的实用函数,用于加载发送到合约的证明数据: ```func -(slice, slice) load_p1(slice body) impure { +(slice, slice) load_p1(slice body) { ... } -(slice, slice) load_p2(slice body) impure { +(slice, slice) load_p2(slice body) { ... } -(slice, int) load_newint(slice body) impure { +(slice, int) load_newint(slice body) { ... } ``` @@ -285,7 +285,7 @@ load_data 和 save_data 函数仅用于加载和保存证明验证结果(仅 int pubInput0 -) impure { +) { slice cpub = bls_g1_multiexp( @@ -303,7 +303,7 @@ load_data 和 save_data 函数仅用于加载和保存证明验证结果(仅 pi_c, vk_delta_2, vk_alpha_1, vk_beta_2, 4); - ;; ctx_res = a; + // ctx_res = a; if (a == 0) { ctx_res = 0; } else { @@ -591,4 +591,4 @@ Ran all test suites. ## 📬 关于作者 -- Saber的链接: [Telegram](https://t.me/saber_coder), [Github](https://github.com/saberdotcoder) 和 [LinkedIn](https://www.linkedin.com/in/szafarpoor/) \ No newline at end of file +- Saber的链接: [Telegram](https://t.me/saber_coder), [Github](https://github.com/saberdotcoder) 和 [LinkedIn](https://www.linkedin.com/in/szafarpoor/) diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/data-formats/msg-tlb.mdx b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/data-formats/msg-tlb.mdx index 3eaecb9edf..2a882b5e9d 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/data-formats/msg-tlb.mdx +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/data-formats/msg-tlb.mdx @@ -336,21 +336,21 @@ var_int$_ {n:#} len:(#< n) value:(int (len * 8)) ```func var msg = begin_cell() - .store_uint(0, 1) ;; 标记 - .store_uint(1, 1) ;; ihr_disabled - .store_uint(1, 1) ;; 允许弹回 - .store_uint(0, 1) ;; 本身不是弹回 + .store_uint(0, 1) // 标记 + .store_uint(1, 1) // ihr_disabled + .store_uint(1, 1) // 允许弹回 + .store_uint(0, 1) // 本身不是弹回 .store_slice(source) .store_slice(destination) - ;; 序列化 CurrencyCollection(见下文) + // 序列化 CurrencyCollection(见下文) .store_coins(amount) .store_dict(extra_currencies) - .store_coins(0) ;; ihr_fee - .store_coins(fwd_value) ;; fwd_fee - .store_uint(cur_lt(), 64) ;; 交易的 lt - .store_uint(now(), 32) ;; 交易的 unixtime - .store_uint(0, 1) ;; 没有 init-field 标志位(Maybe) - .store_uint(0, 1) ;; 原位消息体标志位(Either) + .store_coins(0) // ihr_fee + .store_coins(fwd_value) // fwd_fee + .store_uint(cur_lt(), 64) // 交易的 lt + .store_uint(now(), 32) // 交易的 unixtime + .store_uint(0, 1) // 没有 init-field 标志位(Maybe) + .store_uint(0, 1) // 原位消息体标志位(Either) .store_slice(msg_body) .end_cell(); ``` diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/fift/fift-and-tvm-assembly.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/fift/fift-and-tvm-assembly.md index 6aa902592b..73cb15b1ae 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/fift/fift-and-tvm-assembly.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/fift/fift-and-tvm-assembly.md @@ -15,7 +15,7 @@ x{6F02} dup @Defop PAIR @Defop CONS ``` > Asm.fif 中的 TVM 操作码定义 -``` +```fift "Asm.fif" include <{ SETCP0 DUP IFNOTRET // return if recv_internal DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods @@ -71,7 +71,7 @@ contract: ``` 现在,你可以从智能合约中提取这个 blob: -```fift +```func cell load_blob() asm "LDBLOB"; () recv_internal() { @@ -82,11 +82,11 @@ cell load_blob() asm "LDBLOB"; ### [TVM 汇编] - 将整数转换为字符串 遗憾的是,尝试使用 Fift 原语进行 int-to-string 转换失败。 -```fift +```func slice int_to_string(int x) asm "(.) $>s PUSHSLICE"; ``` 原因很明显:Fift 在编译时进行计算,那时还没有 `x` 可供转换。要将非常量整数转换为字符串切片,你需要 TVM 汇编。例如,这是 TON 智能挑战 3 参赛者之一的代码: -```fift +```func tuple digitize_number(int value) asm "NIL WHILE:<{ OVER }>DO<{ SWAP TEN DIVMOD s1 s2 XCHG TPUSH }> NIP"; @@ -106,16 +106,16 @@ builder store_signed(builder msg, int v) inline_ref { ### [TVM 汇编] - 低成本的模乘 -```fift -int mul_mod(int a, int b, int m) inline_ref { ;; 1232 gas 单位 +```func +int mul_mod(int a, int b, int m) inline_ref { // 1232 gas 单位 (_, int r) = muldivmod(a % m, b % m, m); return r; } -int mul_mod_better(int a, int b, int m) inline_ref { ;; 1110 gas 单位 +int mul_mod_better(int a, int b, int m) inline_ref { // 1110 gas 单位 (_, int r) = muldivmod(a, b, m); return r; } -int mul_mod_best(int a, int b, int m) asm "x{A988} s,"; ;; 65 gas 单位 +int mul_mod_best(int a, int b, int m) asm "x{A988} s,"; // 65 gas 单位 ``` `x{A988}` 是根据 [5.2 Division](/learn/tvm-instructions/instructions#52-division) 格式化的操作码:带有预乘法的除法,唯一返回的结果是第三个参数的余数。但操作码需要进入智能合约代码 - 这就是 `s,` 的作用:它将栈顶的切片存储到稍低的构建器中。 diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/changelog.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/changelog.md index e51041be58..2ff0148956 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/changelog.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/changelog.md @@ -40,10 +40,10 @@ 在这个版本中增加了: - [try/catch 语句](/develop/func/statements#try-catch-statements) -- [throw_arg 函数](/develop/func/builtins#throwing-exceptions) +- [throw_arg 函数](/develop/func/stdlib#throwing-exceptions) - 允许就地修改和批量赋值全局变量:`a~inc()` 和 `(a, b) = (3, 5)`,其中 `a` 是全局变量 修复: - 禁止在同一表达式中使用局部变量后对其进行模糊修改:`var x = (ds, ds~load_uint(32), ds~load_unit(64));` 是禁止的,而 `var x = (ds~load_uint(32), ds~load_unit(64), ds);` 是允许的 - 允许空的内联函数 -- 修复罕见的 `while` 优化错误 \ No newline at end of file +- 修复罕见的 `while` 优化错误 diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/cookbook.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/cookbook.md index 7bc116eba5..e6acde7812 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/cookbook.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/cookbook.md @@ -10,13 +10,13 @@ 假设我们想检查某个事件是否相关。为此,我们使用标志变量。记住在 FunC 中 `true` 是 `-1` 而 `false` 是 `0`。 ```func -int flag = 0; ;; false +int flag = 0; // false if (flag) { - ;; 做一些事情 + // 做一些事情 } else { - ;; 拒绝交易 + // 拒绝交易 } ``` @@ -52,19 +52,19 @@ repeat(degree - 1) { 当我们不知道要执行特定操作多少次时,while 循环很有用。例如,取一个 `cell`,我们知道它可以存储最多四个对其他 cell 的引用。 ```func -cell inner_cell = begin_cell() ;; 创建一个新的空构建器 - .store_uint(123, 16) ;; 存储值为 123 且长度为 16 位的 uint - .end_cell(); ;; 将构建器转换为 cell +cell inner_cell = begin_cell() // 创建一个新的空构建器 + .store_uint(123, 16) // 存储值为 123 且长度为 16 位的 uint + .end_cell(); // 将构建器转换为 cell cell message = begin_cell() - .store_ref(inner_cell) ;; 将 cell 作为引用存储 + .store_ref(inner_cell) // 将 cell 作为引用存储 .store_ref(inner_cell) .end_cell(); -slice msg = message.begin_parse(); ;; 将 cell 转换为 slice -while (msg.slice_refs_empty?() != -1) { ;; 我们应该记住 -1 是 true - cell inner_cell = msg~load_ref(); ;; 从 slice msg 中加载 cell - ;; 做一些事情 +slice msg = message.begin_parse(); // 将 cell 转换为 slice +while (msg.slice_refs_empty?() != -1) { // 我们应该记住 -1 是 true + cell inner_cell = msg~load_ref(); // 从 slice msg 中加载 cell + // 做一些事情 } ``` @@ -92,8 +92,8 @@ while (msg.slice_refs_empty?() != -1) { ;; 我们应该记住 -1 是 true int flag = 0; do { - ;; 即使 flag 是 false (0) 也做一些事情 -} until (flag == -1); ;; -1 是 true + // 即使 flag 是 false (0) 也做一些事情 +} until (flag == -1); // -1 是 true ``` > 💡 有用的链接 @@ -105,31 +105,31 @@ do { 在处理 `slice` 之前,需要检查它是否有数据以便正确处理。我们可以使用 `slice_empty?()` 来做到这一点,但我们必须考虑到,如果有至少一个 `bit` 的数据或一个 `ref`,它将返回 `-1`(`true`)。 ```func -;; 创建空 slice +// 创建空 slice slice empty_slice = ""; -;; `slice_empty?()` 返回 `true`,因为 slice 没有任何 `bits` 和 `refs` +// `slice_empty?()` 返回 `true`,因为 slice 没有任何 `bits` 和 `refs` empty_slice.slice_empty?(); -;; 创建仅包含 bits 的 slice +// 创建仅包含 bits 的 slice slice slice_with_bits_only = "Hello, world!"; -;; `slice_empty?()` 返回 `false`,因为 slice 有 `bits` +// `slice_empty?()` 返回 `false`,因为 slice 有 `bits` slice_with_bits_only.slice_empty?(); -;; 创建仅包含 refs 的 slice +// 创建仅包含 refs 的 slice slice slice_with_refs_only = begin_cell() .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_empty?()` 返回 `false`,因为 slice 有 `refs` +// `slice_empty?()` 返回 `false`,因为 slice 有 `refs` slice_with_refs_only.slice_empty?(); -;; 创建包含 bits 和 refs 的 slice +// 创建包含 bits 和 refs 的 slice slice slice_with_bits_and_refs = begin_cell() .store_slice("Hello, world!") .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_empty?()` 返回 `false`,因为 slice 有 `bits` 和 `refs` +// `slice_empty?()` 返回 `false`,因为 slice 有 `bits` 和 `refs` slice_with_bits_and_refs.slice_empty?(); ``` > 💡 有用的链接 @@ -152,31 +152,31 @@ slice_with_bits_and_refs.slice_empty?(); 如果我们只需要检查 `bits`,不关心 `slice` 中是否有任何 `refs`,那么我们应该使用 `slice_data_empty?()`。 ```func -;; 创建空 slice +// 创建空 slice slice empty_slice = ""; -;; `slice_data_empty?()` 返回 `true`,因为 slice 没有任何 `bits` +// `slice_data_empty?()` 返回 `true`,因为 slice 没有任何 `bits` empty_slice.slice_data_empty?(); -;; 创建仅包含 bits 的 slice +// 创建仅包含 bits 的 slice slice slice_with_bits_only = "Hello, world!"; -;; `slice_data_empty?()` 返回 `false`,因为 slice 有 `bits` +// `slice_data_empty?()` 返回 `false`,因为 slice 有 `bits` slice_with_bits_only.slice_data_empty?(); -;; 创建仅包含 refs 的 slice +// 创建仅包含 refs 的 slice slice slice_with_refs_only = begin_cell() .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_data_empty?()` 返回 `true`,因为 slice 没有 `bits` +// `slice_data_empty?()` 返回 `true`,因为 slice 没有 `bits` slice_with_refs_only.slice_data_empty?(); -;; 创建包含 bits 和 refs 的 slice +// 创建包含 bits 和 refs 的 slice slice slice_with_bits_and_refs = begin_cell() .store_slice("Hello, world!") .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_data_empty?()` 返回 `false`,因为 slice 有 `bits` +// `slice_data_empty?()` 返回 `false`,因为 slice 有 `bits` slice_with_bits_and_refs.slice_data_empty?(); ``` @@ -199,31 +199,31 @@ slice_with_bits_and_refs.slice_data_empty?(); 如果我们只对 `refs` 感兴趣,我们应该使用 `slice_refs_empty?()` 来检查它们的存在。 ```func -;; 创建空 slice +// 创建空 slice slice empty_slice = ""; -;; `slice_refs_empty?()` 返回 `true`,因为 slice 没有任何 `refs` +// `slice_refs_empty?()` 返回 `true`,因为 slice 没有任何 `refs` empty_slice.slice_refs_empty?(); -;; 创建只包含 bits 的 slice +// 创建只包含 bits 的 slice slice slice_with_bits_only = "Hello, world!"; -;; `slice_refs_empty?()` 返回 `true`,因为 slice 没有任何 `refs` +// `slice_refs_empty?()` 返回 `true`,因为 slice 没有任何 `refs` slice_with_bits_only.slice_refs_empty?(); -;; 创建只包含 refs 的 slice +// 创建只包含 refs 的 slice slice slice_with_refs_only = begin_cell() .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_refs_empty?()` 返回 `false`,因为 slice 有 `refs` +// `slice_refs_empty?()` 返回 `false`,因为 slice 有 `refs` slice_with_refs_only.slice_refs_empty?(); -;; 创建包含 bits 和 refs 的 slice +// 创建包含 bits 和 refs 的 slice slice slice_with_bits_and_refs = begin_cell() .store_slice("Hello, world!") .store_ref(null()) .end_cell() .begin_parse(); -;; `slice_refs_empty?()` 返回 `false`,因为 slice 有 `refs` +// `slice_refs_empty?()` 返回 `false`,因为 slice 有 `refs` slice_with_bits_and_refs.slice_refs_empty?(); ``` @@ -251,15 +251,15 @@ cell cell_with_bits_and_refs = begin_cell() .store_ref(null()) .end_cell(); -;; 将 `cell` 类型更改为 slice,使用 `begin_parse()` +// 将 `cell` 类型更改为 slice,使用 `begin_parse()` slice cs = cell_with_bits_and_refs.begin_parse(); -;; 确定 slice 是否为空 +// 确定 slice 是否为空 if (cs.slice_empty?()) { - ;; cell 为空 + // cell 为空 } else { - ;; cell 不为空 + // cell 不为空 } ``` @@ -284,11 +284,11 @@ cell d = new_dict(); d~udict_set(256, 0, "hello"); d~udict_set(256, 1, "world"); -if (d.dict_empty?()) { ;; 确定 dict 是否为空 - ;; dict 为空 +if (d.dict_empty?()) { // 确定 dict 是否为空 + // dict 为空 } else { - ;; dict 不为空 + // dict 不为空 } ``` @@ -307,7 +307,7 @@ func/stdlib/#dict_set) 为 dict d 添加一些元素,所以它不为空 在处理 `tuples` 时,始终知道内部是否有值以供提取是很重要的。如果我们尝试从空的 `tuple` 中提取值,将会得到一个错误:“not a tuple of valid size”,exit code 7。 ```func -;; 声明 tlen 函数,因为它在 stdlib 中没有提供 +// 声明 tlen 函数,因为它在 stdlib 中没有提供 (int) tlen (tuple t) asm "TLEN"; () main () { @@ -316,10 +316,10 @@ func/stdlib/#dict_set) 为 dict d 添加一些元素,所以它不为空 t~tpush(37); if (t.tlen() == 0) { - ;; tuple 为空 + // tuple 为空 } else { - ;; tuple 不为空 + // tuple 不为空 } } ``` @@ -343,9 +343,9 @@ tuple numbers = null(); numbers = cons(100, numbers); if (numbers.null?()) { - ;; Lisp 类型的列表为空 + // Lisp 类型的列表为空 } else { - ;; Lisp 类型的列表不为空 + // Lisp 类型的列表不为空 } ``` @@ -356,19 +356,19 @@ if (numbers.null?()) { 假设我们有一个 `counter`,用于存储交易次数。在智能合约状态的第一次交易中,这个变量不可用,因为状态为空,因此需要处理这种情况。如果状态为空,我们创建一个变量 `counter` 并保存它。 ```func -;; `get_data()` 将从合约状态返回数据 cell +// `get_data()` 将从合约状态返回数据 cell cell contract_data = get_data(); slice cs = contract_data.begin_parse(); if (cs.slice_empty?()) { - ;; 合约数据为空,所以我们创建 counter 并保存 + // 合约数据为空,所以我们创建 counter 并保存 int counter = 1; - ;; 创建 cell,添加 counter 并保存在合约状态中 + // 创建 cell,添加 counter 并保存在合约状态中 set_data(begin_cell().store_uint(counter, 32).end_cell()); } else { - ;; 合约数据不为空,所以我们获取我们的 counter,增加它并保存 - ;; 我们应该指定 counter 的正确的位长度 + // 合约数据不为空,所以我们获取我们的 counter,增加它并保存 + // 我们应该指定 counter 的正确的位长度 int counter = cs~load_uint(32) + 1; set_data(begin_cell().store_uint(counter, 32).end_cell()); } @@ -393,20 +393,20 @@ else { 如果我们希望合约发送一个内部消息,我们应该首先正确地创建它为一个 cell,指定技术标志位、接收地址和其余数据。 ```func -;; 我们使用字面量 `a` 从包含地址的字符串中获取有效地址的 slice +// 我们使用字面量 `a` 从包含地址的字符串中获取有效地址的 slice slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; int amount = 1000000000; -;; 我们使用 `op` 来识别操作 +// 我们使用 `op` 来识别操作 int op = 0; cell msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息 header 部(参见发送消息页面) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息 header 部(参见发送消息页面) .store_uint(op, 32) .end_cell(); -send_raw_message(msg, 3); ;; 模式 3 - 分别支付费用并忽略错误 +send_raw_message(msg, 3); // 模式 3 - 分别支付费用并忽略错误 ``` > 💡 注意 @@ -438,11 +438,11 @@ send_raw_message(msg, 3); ;; 模式 3 - 分别支付费用并忽略错误 如果我们确信有足够的空间,我们也可以在与 header 相同的 `cell` 中发送消息体。在这种情况下,我们需要将位设置为 `0`。 ```func -;; 我们使用字面量 `a` 从包含地址的字符串中获取有效地址的 slice +// 我们使用字面量 `a` 从包含地址的字符串中获取有效地址的 slice slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; int amount = 1000000000; int op = 0; -cell message_body = begin_cell() ;; 创建包含消息的 cell +cell message_body = begin_cell() // 创建包含消息的 cell .store_uint(op, 32) .store_slice("❤") .end_cell(); @@ -451,12 +451,12 @@ cell msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) ;; 默认消息 header 部(参见发送消息页面) - .store_uint(1, 1) ;; 设置位为 1,表明 cell 将继续传输 + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) // 默认消息 header 部(参见发送消息页面) + .store_uint(1, 1) // 设置位为 1,表明 cell 将继续传输 .store_ref(message_body) .end_cell(); -send_raw_message(msg, 3); ;; mode 3 - 分别支付费用并忽略错误 +send_raw_message(msg, 3); // mode 3 - 分别支付费用并忽略错误 ``` > 💡 注意 @@ -490,7 +490,7 @@ send_raw_message(msg, 3); ;; mode 3 - 分别支付费用并忽略错误 发送消息时,消息体可以作为 `cell` 或 `slice` 发送。在这个例子中,我们将消息体放在 `slice` 内部发送。 ```func -;; 我们使用字面量 `a` 从包含地址的字符串中获取有效地址的 slice +// 我们使用字面量 `a` 从包含地址的字符串中获取有效地址的 slice slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; int amount = 1000000000; int op = 0; @@ -500,12 +500,12 @@ cell msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息 header 部(参见发送消息页面) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息 header 部(参见发送消息页面) .store_uint(op, 32) .store_slice(message_body) .end_cell(); -send_raw_message(msg, 3); ;; +send_raw_message(msg, 3); // mode 3 - 分别支付费用并忽略错误 ``` @@ -537,14 +537,14 @@ forall X -> (tuple) to_tuple (X x) asm "NOP"; int i = 0; while (i < len) { int x = t.at(i); - ;; 使用 x 做一些事情 + // 使用 x 做一些事情 i = i + 1; } i = len - 1; while (i >= 0) { int x = t.at(i); - ;; 使用 x 做一些事情 + // 使用 x 做一些事情 i = i - 1; } } @@ -562,7 +562,7 @@ forall X -> (tuple) to_tuple (X x) asm "NOP"; 例如,我们有 `tpush` 方法,它可以向 `tuple` 中添加元素,但没有 `tpop`。在这种情况下,我们应该这样做: ```func -;; ~ 表示它是修改方法 +// ~ 表示它是修改方法 forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; ``` @@ -600,10 +600,10 @@ forall X -> tuple cast_to_tuple (X x) forall X -> int cast_to_int (X x) asm "NOP"; forall X -> (tuple) to_tuple (X x) asm "NOP"; -;; 定义全局变量 +// 定义全局变量 global int max_value; -() iterate_tuple (tuple t) impure { +() iterate_tuple (tuple t) { repeat (t.tuple_length()) { var value = t~tpop(); if (is_tuple(value)) { @@ -621,9 +621,9 @@ global int max_value; () main () { tuple t = to_tuple([[2,6], [1, [3, [3, 5]]], 3]); int len = t.tuple_length(); - max_value = 0; ;; 重置 max_value; - iterate_tuple(t); ;; 迭代 tuple 并找到最大值 - ~dump(max_value); ;; 6 + max_value = 0; // 重置 max_value; + iterate_tuple(t); // 迭代 tuple 并找到最大值 + ~dump(max_value); // 6 } ``` @@ -631,7 +631,7 @@ global int max_value; > > [文档中的“Global variables”](/develop/func/global_variables) > -> [文档中的“~dump”](/develop/func/builtins#dump-variable) +> [文档中的“~dump”](/develop/func/stdlib#debug-primitives) > > [文档中的“TVM instructions”](/learn/tvm-instructions/instructions) @@ -642,25 +642,25 @@ global int max_value; forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; () main () { - ;; 创建一个空的 tuple + // 创建一个空的 tuple tuple names = empty_tuple(); - ;; 添加新项目 + // 添加新项目 names~tpush("Naito Narihira"); names~tpush("Shiraki Shinichi"); names~tpush("Akamatsu Hachemon"); names~tpush("Takaki Yuichi"); - ;; 弹出最后一项 + // 弹出最后一项 slice last_name = names~tpop(); - ;; 获取第一项 + // 获取第一项 slice first_name = names.first(); - ;; 按索引获取项 + // 按索引获取项 slice best_name = names.at(2); - ;; 获取列表长度 + // 获取列表长度 int number_names = names.tlen(); } ``` @@ -681,37 +681,37 @@ forall X -> slice cast_to_slice (X x) asm "NOP"; forall X -> tuple cast_to_tuple (X x) asm "NOP"; forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; -forall X -> () resolve_type (X value) impure { - ;; value 是类型 X,由于我们不知道确切的值是什么 - 我们需要检查值然后转换它 +forall X -> () resolve_type (X value) { + // value 是类型 X,由于我们不知道确切的值是什么 - 我们需要检查值然后转换它 if (is_null(value)) { - ;; 对 null 做一些事情 + // 对 null 做一些事情 } elseif (is_int(value)) { int valueAsInt = cast_to_int(value); - ;; 对 int 做一些事情 + // 对 int 做一些事情 } elseif (is_slice(value)) { slice valueAsSlice = cast_to_slice(value); - ;; 对 slice 做一些事情 + // 对 slice 做一些事情 } elseif (is_cell(value)) { cell valueAsCell = cast_to_cell(value); - ;; 对 cell 做一些事情 + // 对 cell 做一些事情 } elseif (is_tuple(value)) { tuple valueAsTuple = cast_to_tuple(value); - ;; 对 tuple 做一些事情 + // 对 tuple 做一些事情 } } () main () { - ;; 创建一个空的 tuple + // 创建一个空的 tuple tuple stack = empty_tuple(); - ;; 假设我们有一个 tuple 并且不知道它们的确切类型 + // 假设我们有一个 tuple 并且不知道它们的确切类型 stack~tpush("Some text"); stack~tpush(4); - ;; 我们使用 var 因为我们不知道值的类型 + // 我们使用 var 因为我们不知道值的类型 var value = stack~tpop(); resolve_type(value); } @@ -728,7 +728,7 @@ forall X -> () resolve_type (X value) impure { int current_time = now(); if (current_time > 1672080143) { - ;; 做一些事情 + // 做一些事情 } ``` @@ -741,7 +741,7 @@ if (current_time > 1672080143) { ::: ```func -randomize_lt(); ;; 只需做一次 +randomize_lt(); // 只需做一次 int a = rand(10); int b = rand(1000000); @@ -754,12 +754,12 @@ int c = random(); ```func (int) modulo_operations (int xp, int zp) { - ;; 2^255 - 19 是蒙哥马利曲线的素数,意味着所有操作都应该对其素数进行 + // 2^255 - 19 是蒙哥马利曲线的素数,意味着所有操作都应该对其素数进行 int prime = 57896044618658097711785492504343953926634992332820282019728792003956564819949; - ;; muldivmod 自身处理以下两行 - ;; int xp+zp = (xp + zp) % prime; - ;; int xp-zp = (xp - zp + prime) % prime; + // muldivmod 自身处理以下两行 + // int xp+zp = (xp + zp) % prime; + // int xp-zp = (xp - zp + prime) % prime; (_, int xp+zp*xp-zp) = muldivmod(xp + zp, xp - zp, prime); return xp+zp*xp-zp; } @@ -775,11 +775,11 @@ int c = random(); ```func int number = 198; -throw_if(35, number > 50); ;; 只有当数字大于 50 时才会触发错误 +throw_if(35, number > 50); // 只有当数字大于 50 时才会触发错误 -throw_unless(39, number == 198); ;; 只有当数字不等于 198 时才会触发错误 +throw_unless(39, number == 198); // 只有当数字不等于 198 时才会触发错误 -throw(36); ;; 无论如何都会触发错误 +throw(36); // 无论如何都会触发错误 ``` [标准 TVM 异常代码](/learn/tvm-instructions/tvm-exit-codes.md) @@ -805,7 +805,7 @@ forall X -> (tuple) to_tuple (X x) asm "NOP"; () main () { tuple t = to_tuple([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); tuple reversed_t = reverse_tuple(t); - ~dump(reversed_t); ;; [10 9 8 7 6 5 4 3 2 1] + ~dump(reversed_t); // [10 9 8 7 6 5 4 3 2 1] } ``` @@ -840,11 +840,11 @@ int tlen (tuple t) asm "TLEN"; numbers~tpush(999); numbers~tpush(54); - ~dump(numbers); ;; [19 999 54] + ~dump(numbers); // [19 999 54] numbers~remove_item(1); - ~dump(numbers); ;; [19 54] + ~dump(numbers); // [19 54] } ### 判断切片是否相等 @@ -861,12 +861,12 @@ int are_slices_equal_2? (slice a, slice b) asm "SDEQ"; () main () { slice a = "Some text"; slice b = "Some text"; - ~dump(are_slices_equal_1?(a, b)); ;; -1 = true + ~dump(are_slices_equal_1?(a, b)); // -1 = true a = "Text"; - ;; 我们使用字面量 `a` 来从包含地址的字符串中获取切片的有效地址 + // 我们使用字面量 `a` 来从包含地址的字符串中获取切片的有效地址 b = "EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"a; - ~dump(are_slices_equal_2?(a, b)); ;; 0 = false + ~dump(are_slices_equal_2?(a, b)); // 0 = false } ``` @@ -893,7 +893,7 @@ int are_cells_equal? (cell a, cell b) { .store_uint(123, 16) .end_cell(); - ~dump(are_cells_equal?(a, b)); ;; -1 = true + ~dump(are_cells_equal?(a, b)); // -1 = true } ``` @@ -924,10 +924,10 @@ int are_cells_equal? (cell a, cell b) { } (int) are_tuples_equal? (tuple t1, tuple t2) { - int equal? = -1; ;; 初始值为 true + int equal? = -1; // 初始值为 true if (t1.tuple_length() != t2.tuple_length()) { - ;; 如果元组长度不同,它们就不能相等 + // 如果元组长度不同,它们就不能相等 return 0; } @@ -938,7 +938,7 @@ int are_cells_equal? (cell a, cell b) { var v2 = t2~tpop(); if (is_null(t1) & is_null(t2)) { - ;; nulls are always equal + // nulls are always equal } elseif (is_int(v1) & is_int(v2)) { if (cast_to_int(v1) != cast_to_int(v2)) { @@ -958,7 +958,7 @@ int are_cells_equal? (cell a, cell b) { } } elseif (is_tuple(v1) & is_tuple(v2)) { - ;; 递归地判断嵌套元组 + // 递归地判断嵌套元组 if (~ are_tuples_equal?(cast_to_tuple(v1), cast_to_tuple(v2))) { equal? = 0; } @@ -977,7 +977,7 @@ int are_cells_equal? (cell a, cell b) { tuple t1 = cast_to_tuple([[2, 6], [1, [3, [3, 5]]], 3]); tuple t2 = cast_to_tuple([[2, 6], [1, [3, [3, 5]]], 3]); - ~dump(are_tuples_equal?(t1, t2)); ;; -1 + ~dump(are_tuples_equal?(t1, t2)); // -1 } ``` @@ -995,19 +995,19 @@ int are_cells_equal? (cell a, cell b) { ```func (slice) generate_internal_address (int workchain_id, cell state_init) { - ;; addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; + // addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; return begin_cell() - .store_uint(2, 2) ;; addr_std$10 - .store_uint(0, 1) ;; anycast nothing - .store_int(workchain_id, 8) ;; workchain_id: -1 + .store_uint(2, 2) // addr_std$10 + .store_uint(0, 1) // anycast nothing + .store_int(workchain_id, 8) // workchain_id: -1 .store_uint(cell_hash(state_init), 256) .end_cell().begin_parse(); } () main () { slice deploy_address = generate_internal_address(workchain(), state_init); - ;; then we can deploy new contract + // then we can deploy new contract } ``` @@ -1027,12 +1027,12 @@ int are_cells_equal? (cell a, cell b) { (int) ubitsize (int a) asm "UBITSIZE"; slice generate_external_address (int address) { - ;; addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt; + // addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt; int address_length = ubitsize(address); return begin_cell() - .store_uint(1, 2) ;; addr_extern$01 + .store_uint(1, 2) // addr_extern$01 .store_uint(address_length, 9) .store_uint(address, address_length) .end_cell().begin_parse(); @@ -1082,14 +1082,14 @@ set_data(begin_cell().store_dict(dictionary_cell).end_cell()); ```func cell msg = begin_cell() - .store_uint(0x18, 6) ;; 标志位 - .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; 目的地址 - .store_coins(100) ;; 发送的nanoTons数量 - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息header(参见发送消息页面) - .store_uint(0, 32) ;; 零操作码 - 表示带评论的简单转账消息 - .store_slice("Hello from FunC!") ;; 评论 + .store_uint(0x18, 6) // 标志位 + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) // 目的地址 + .store_coins(100) // 发送的nanoTons数量 + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息header(参见发送消息页面) + .store_uint(0, 32) // 零操作码 - 表示带评论的简单转账消息 + .store_slice("Hello from FunC!") // 评论 .end_cell(); -send_raw_message(msg, 3); ;; mode 3 - 分开支付费用,忽略错误 +send_raw_message(msg, 3); // mode 3 - 分开支付费用,忽略错误 ``` > 💡 有用的链接 @@ -1102,20 +1102,20 @@ send_raw_message(msg, 3); ;; mode 3 - 分开支付费用,忽略错误 ```func () recv_internal (slice in_msg_body) { - {- + /* 这是一个代理合约的简单示例。 它将期望 in_msg_body 包含消息 mode、body 和要发送到的目的地址。 - -} + */ - int mode = in_msg_body~load_uint(8); ;; 第一个字节将包含消息 mode - slice addr = in_msg_body~load_msg_addr(); ;; 然后我们解析目的地址 - slice body = in_msg_body; ;; in_msg_body 中剩余的所有内容将是我们新消息的 body + int mode = in_msg_body~load_uint(8); // 第一个字节将包含消息 mode + slice addr = in_msg_body~load_msg_addr(); // 然后我们解析目的地址 + slice body = in_msg_body; // in_msg_body 中剩余的所有内容将是我们新消息的 body cell msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) - .store_coins(100) ;; 仅作示例 - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息 header (参见发送消息页面) + .store_coins(100) // 仅作示例 + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息 header (参见发送消息页面) .store_slice(body) .end_cell(); send_raw_message(msg, mode); @@ -1134,14 +1134,14 @@ send_raw_message(msg, 3); ;; mode 3 - 分开支付费用,忽略错误 ```func cell msg = begin_cell() - .store_uint(0x18, 6) ;; 标志位 - .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; 目的地址 - .store_coins(0) ;; 我们现在不关心这个值 - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息 header (参见发送消息页面) - .store_uint(0, 32) ;; 零操作码 - 表示带评论的简单转账消息 - .store_slice("Hello from FunC!") ;; 评论 + .store_uint(0x18, 6) // 标志位 + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) // 目的地址 + .store_coins(0) // 我们现在不关心这个值 + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息 header (参见发送消息页面) + .store_uint(0, 32) // 零操作码 - 表示带评论的简单转账消息 + .store_slice("Hello from FunC!") // 评论 .end_cell(); -send_raw_message(msg, 128); ;; 模式=128 用于携带当前智能合约剩余全部余额的消息 +send_raw_message(msg, 128); // 模式=128 用于携带当前智能合约剩余全部余额的消息 ``` > 💡 有用的链接 @@ -1155,14 +1155,14 @@ send_raw_message(msg, 128); ;; 模式=128 用于携带当前智能合约剩余 我们知道,单个 `cell` (<1023 bits) 中只能容纳 127 个字符。如果我们需要更多 - 我们需要组织蛇形cell。 ```func -{- +/* 如果我们想发送带有非常长的评论的消息,我们应该将评论分成几个片段。 每个片段应包含 <1023 位数据(127个字符)。 每个片段应该有一个引用指向下一个,形成蛇形结构。 --} +*/ cell body = begin_cell() - .store_uint(0, 32) ;; 零操作码 - 带评论的简单消息 + .store_uint(0, 32) // 零操作码 - 带评论的简单消息 .store_slice("long long long message...") .store_ref(begin_cell() .store_slice(" you can store string of almost any length here.") @@ -1173,15 +1173,15 @@ cell body = begin_cell() .end_cell(); cell msg = begin_cell() - .store_uint(0x18, 6) ;; 标志位 - ;; 我们使用字面量 `a` 从包含地址的字符串中获取片段内的有效地址 - .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; 目的地址 - .store_coins(100) ;; 发送的nanoTons数量 - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) ;; 默认消息 header (参见发送消息页面) - .store_uint(1, 1) ;; 我们希望将 body 存储为引用 + .store_uint(0x18, 6) // 标志位 + // 我们使用字面量 `a` 从包含地址的字符串中获取片段内的有效地址 + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) // 目的地址 + .store_coins(100) // 发送的nanoTons数量 + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) // 默认消息 header (参见发送消息页面) + .store_uint(1, 1) // 我们希望将 body 存储为引用 .store_ref(body) .end_cell(); -send_raw_message(msg, 3); ;; mode 3 - 分开支付费用,忽略错误 +send_raw_message(msg, 3); // mode 3 - 分开支付费用,忽略错误 ``` > 💡 有用的链接 @@ -1195,8 +1195,8 @@ send_raw_message(msg, 3); ;; mode 3 - 分开支付费用,忽略错误 ```func slice s = begin_cell() .store_slice("Some data bits...") - .store_ref(begin_cell().end_cell()) ;; 一些引用 - .store_ref(begin_cell().end_cell()) ;; 一些引用 + .store_ref(begin_cell().end_cell()) // 一些引用 + .store_ref(begin_cell().end_cell()) // 一些引用 .end_cell().begin_parse(); slice s_only_data = s.preload_bits(s.slice_bits()); @@ -1216,9 +1216,9 @@ slice s_only_data = s.preload_bits(s.slice_bits()); ```func (slice, (int)) load_digit (slice s) { - int x = s~load_uint(8); ;; 从片段中加载 8 位(一个字符) - x -= 48; ;; 字符 '0' 的代码为 48,所以我们减去它以得到数字 - return (s, (x)); ;; 返回我们修改的片段和加载的数字 + int x = s~load_uint(8); // 从片段中加载 8 位(一个字符) + x -= 48; // 字符 '0' 的代码为 48,所以我们减去它以得到数字 + return (s, (x)); // 返回我们修改的片段和加载的数字 } () main () { @@ -1226,7 +1226,7 @@ slice s_only_data = s.preload_bits(s.slice_bits()); int c1 = s~load_digit(); int c2 = s~load_digit(); int c3 = s~load_digit(); - ;; 这里 s 等于 "",c1 = 2,c2 = 5,c3 = 8 + // 这里 s 等于 "",c1 = 2,c2 = 5,c3 = 8 } ``` @@ -1237,7 +1237,7 @@ slice s_only_data = s.preload_bits(s.slice_bits()); ### 如何计算 n 的幂 ```func -;; 未优化版本 +// 未优化版本 int pow (int a, int n) { int i = 0; int value = a; @@ -1250,7 +1250,7 @@ int pow (int a, int n) { ; } -;; 优化版本 +// 优化版本 (int) binpow (int n, int e) { if (e == 0) { return 1; @@ -1268,7 +1268,7 @@ int pow (int a, int n) { () main () { int num = binpow(2, 3); - ~dump(num); ;; 8 + ~dump(num); // 8 } ``` @@ -1280,7 +1280,7 @@ int number = 0; while (~ string_number.slice_empty?()) { int char = string_number~load_uint(8); - number = (number * 10) + (char - 48); ;; 我们使用 ASCII 表 + number = (number * 10) + (char - 48); // 我们使用 ASCII 表 } ~dump(number); @@ -1315,10 +1315,10 @@ d~udict_set(256, 1, "value 1"); d~udict_set(256, 5, "value 2"); d~udict_set(256, 12, "value 3"); -;; 从小到大遍历键 +// 从小到大遍历键 (int key, slice val, int flag) = d.udict_get_min?(256); while (flag) { - ;; 使用 key->val 对,做某些事情 + // 使用 key->val 对,做某些事情 (key, val, flag) = d.udict_get_next?(256, key); } @@ -1346,7 +1346,7 @@ names~udict_set(256, 25, "Bob"); names~udict_delete?(256, 27); (slice val, int key) = names.udict_get?(256, 27); -~dump(val); ;; null() -> 表示在字典中未找到该键 +~dump(val); // null() -> 表示在字典中未找到该键 ``` ### 如何递归遍历cell树 @@ -1359,7 +1359,7 @@ forall X -> (tuple, ()) push_back (tuple tail, X head) asm "CONS"; forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; () main () { - ;; 仅作为示例的一些cell + // 仅作为示例的一些cell cell c = begin_cell() .store_uint(1, 16) .store_ref(begin_cell() @@ -1376,18 +1376,18 @@ forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; .end_cell()) .end_cell(); - ;; 创建一个没有数据的元组,充当栈的角色 + // 创建一个没有数据的元组,充当栈的角色 tuple stack = null(); - ;; 将主cell放入栈中以便在循环中处理 + // 将主cell放入栈中以便在循环中处理 stack~push_back(c); - ;; 在栈不为空时执行 + // 在栈不为空时执行 while (~ stack.is_null()) { - ;; 从栈中获取cell,并将其转换为 slice 以便处理 + // 从栈中获取cell,并将其转换为 slice 以便处理 slice s = stack~pop_back().begin_parse(); - ;; 对 s 数据做一些操作 + // 对 s 数据做一些操作 - ;; 如果当前 slice 有任何 refs,将它们添加到栈中 + // 如果当前 slice 有任何 refs,将它们添加到栈中 repeat (s.slice_refs()) { stack~push_back(s~load_ref()); } @@ -1415,18 +1415,18 @@ forall X -> (tuple, ()) push_back (tuple tail, X head) asm "CONS"; forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; () main () { - ;; 一些示例列表 + // 一些示例列表 tuple l = null(); l~push_back(1); l~push_back(2); l~push_back(3); - ;; 遍历元素 - ;; 注意这种迭代是倒序的 + // 遍历元素 + // 注意这种迭代是倒序的 while (~ l.is_null()) { var x = l~pop_back(); - ;; 对 x 做一些操作 + // 对 x 做一些操作 } } ``` @@ -1440,28 +1440,28 @@ forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; ### 如何发送部署消息(仅使用 stateInit 或使用 stateInit 和 body) ```func -() deploy_with_stateinit(cell message_header, cell state_init) impure { +() deploy_with_stateinit(cell message_header, cell state_init) { var msg = begin_cell() .store_slice(begin_parse(msg_header)) - .store_uint(2 + 1, 2) ;; init:(Maybe (Either StateInit ^StateInit)) - .store_uint(0, 1) ;; body:(Either X ^X) + .store_uint(2 + 1, 2) // init:(Maybe (Either StateInit ^StateInit)) + .store_uint(0, 1) // body:(Either X ^X) .store_ref(state_init) .end_cell(); - ;; mode 64 - 在新消息中携带剩余值 + // mode 64 - 在新消息中携带剩余值 send_raw_message(msg, 64); } -() deploy_with_stateinit_body(cell message_header, cell state_init, cell body) impure { +() deploy_with_stateinit_body(cell message_header, cell state_init, cell body) { var msg = begin_cell() .store_slice(begin_parse(msg_header)) - .store_uint(2 + 1, 2) ;; init:(Maybe (Either StateInit ^StateInit)) - .store_uint(1, 1) ;; body:(Either X ^X) + .store_uint(2 + 1, 2) // init:(Maybe (Either StateInit ^StateInit)) + .store_uint(1, 1) // body:(Either X ^X) .store_ref(state_init) .store_ref(body) .end_cell(); - ;; mode 64 - 在新消息中携带剩余值 + // mode 64 - 在新消息中携带剩余值 send_raw_message(msg, 64); } ``` @@ -1471,11 +1471,11 @@ forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; ```func () build_stateinit(cell init_code, cell init_data) { var state_init = begin_cell() - .store_uint(0, 1) ;; split_depth:(Maybe (## 5)) - .store_uint(0, 1) ;; special:(Maybe TickTock) - .store_uint(1, 1) ;; (Maybe ^Cell) - .store_uint(1, 1) ;; (Maybe ^Cell) - .store_uint(0, 1) ;; (HashmapE 256 SimpleLib) + .store_uint(0, 1) // split_depth:(Maybe (## 5)) + .store_uint(0, 1) // special:(Maybe TickTock) + .store_uint(1, 1) // (Maybe ^Cell) + .store_uint(1, 1) // (Maybe ^Cell) + .store_uint(0, 1) // (HashmapE 256 SimpleLib) .store_ref(init_code) .store_ref(init_data) .end_cell(); @@ -1487,9 +1487,9 @@ forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; ```func () calc_address(cell state_init) { var future_address = begin_cell() - .store_uint(2, 2) ;; addr_std$10 - .store_uint(0, 1) ;; anycast:(Maybe Anycast) - .store_uint(0, 8) ;; workchain_id:int8 - .store_uint(cell_hash(state_init), 256) ;; address:bits256 + .store_uint(2, 2) // addr_std$10 + .store_uint(0, 1) // anycast:(Maybe Anycast) + .store_uint(0, 8) // workchain_id:int8 + .store_uint(cell_hash(state_init), 256) // address:bits256 .end_cell(); } diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/functions.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/functions.md index b08b932d83..238dcd70d4 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/functions.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/functions.md @@ -205,7 +205,7 @@ int z = x.inc(); 例如,在 [stdlib.fc](/develop/func/stdlib) 函数中 ```func -int random() impure asm "RANDU256"; +int random() asm "RANDU256"; ``` 被定义。使用 `impure` 是因为 `RANDU256` 改变了随机数生成器的内部状态。 @@ -215,7 +215,7 @@ int random() impure asm "RANDU256"; 例如,您可以在此示例中像这样使用 `inline`:[ICO-Minter.fc](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/ft/jetton-minter-ICO.fc#L16) ```func -() save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) impure inline { +() save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) inline { set_data(begin_cell() .store_coins(total_supply) .store_slice(admin_address) @@ -235,7 +235,7 @@ TVM 程序中的每个函数都有一个内部整数 id,可以通过该 id 调 例如, ```func -(int, int) get_n_k() method_id { +get (int, int) get_n_k() { (_, int n, int k, _, _, _, _) = unpack_state(); return (n, k); } diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/overview.mdx b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/overview.mdx index 11889452eb..ee129c558e 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/overview.mdx +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/overview.mdx @@ -8,9 +8,9 @@ FunC 是一种领域特定的、类 C 语言的、静态类型语言。 这是一个用 FunC 编写的发送资金的简单示例方法: ```func -() send_money(slice address, int amount) impure inline { +() send_money(slice address, int amount) inline { var msg = begin_cell() - .store_uint(0x10, 6) ;; nobounce + .store_uint(0x10, 6) // nobounce .store_slice(address) .store_coins(amount) .end_cell(); @@ -71,7 +71,7 @@ npm create ton@latest - [🚩 挑战 6:分析 Getgems 市场上的 NFT 销售](https://github.com /romanovichim/TONQuest6) -* [Func & Blueprint](https://www.youtube.com/watch?v=7omBDfSqGfA&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) 由 **@MarcoDaTr0p0je** 提供 +* [FunC & Blueprint](https://www.youtube.com/watch?v=7omBDfSqGfA&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) 由 **@MarcoDaTr0p0je** 提供 * [Learn FunC in Y Minutes](https://learnxinyminutes.com/docs/func/) 由 **@romanovichim** 提供 * [TON Hello World:编写您的第一个智能合约的逐步指南](https://ton-community.github.io/tutorials/02-contract/) * [TON Hello World:测试您的第一个智能合约的逐步指南](https://ton-community.github.io/tutorials/04-testing/) diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/statements.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/statements.md index a142018db8..fc15011f06 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/statements.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/statements.md @@ -36,10 +36,10 @@ int x = 0; int i = 0; while (i < 10) { (int, int) x = (i, i + 1); - ;; 这里 x 是类型为 (int, int) 的变量 + // 这里 x 是类型为 (int, int) 的变量 i += 1; } -;; 这里 x 是类型为 int 的(不同)变量 +// 这里 x 是类型为 int 的(不同)变量 ``` 但如在全局变量[章节](/develop/func/global_variables.md)中提到的,不允许重新声明全局变量。 @@ -56,7 +56,7 @@ int y = (int x = 3) + 1; ### 函数应用 函数调用看起来像在常规语言中那样。函数调用的参数在函数名之后列出,用逗号分隔。 ```func -;; 假设 foo 的类型为 (int, int, int) -> int +// 假设 foo 的类型为 (int, int, int) -> int int x = foo(1, 2, 3); ``` @@ -74,12 +74,12 @@ int x = 也可以进行 Haskell 类型的调用,但不总是可行(稍后修复): ```func -;; 假设 foo 的类型为 int -> int -> int -> int -;; 即它是柯里化的 +// 假设 foo 的类型为 int -> int -> int -> int +// 即它是柯里化的 (int a, int b, int c) = (1, 2, 3); -int x = foo a b c; ;; ok -;; int y = foo 1 2 3; 不会编译 -int y = foo (1) (2) (3); ;; ok +int x = foo a b c; // ok +// int y = foo 1 2 3; 不会编译 +int y = foo (1) (2) (3); // ok ``` ### Lambda 表达式 暂不支持 Lambda 表达式。 @@ -225,21 +225,21 @@ int x = 1; repeat(10) { x *= 2; } -;; x = 1024 +// x = 1024 ``` ```func int x = 1, y = 10; repeat(y + 6) { x *= 2; } -;; x = 65536 +// x = 65536 ``` ```func int x = 1; repeat(-1) { x *= 2; } -;; x = 1 +// x = 1 ``` 如果次数小于 `-2^31` 或大于 `2^31 - 1`,将抛出范围检查异常。 ### While 循环 @@ -249,7 +249,7 @@ int x = 2; while (x < 100) { x = x * x; } -;; x = 256 +// x = 256 ``` 请注意,条件 `x < 100` 的真值是类型为 `int` 的(参见[没有布尔类型](/develop/func/types#absence-of-boolean-type))。 @@ -260,24 +260,24 @@ int x = 0; do { x += 3; } until (x % 17 == 0); -;; x = 51 +// x = 51 ``` ## If 语句 示例: ```func -;; 通常的 if +// 通常的 if if (flag) { do_something(); } ``` ```func -;; 等同于 if (~ flag) +// 等同于 if (~ flag) ifnot (flag) { do_something(); } ``` ```func -;; 通常的 if-else +// 通常的 if-else if (flag) { do_something(); } @@ -286,7 +286,7 @@ else { } ``` ```func -;; 一些特定功能 +// 一些特定功能 if (flag1) { do_something1(); } else { @@ -300,13 +300,13 @@ if (flag1) ``` ## Try-Catch 语句 -*自 func v0.4.0 起可用* +*自 FunC v0.4.0 起可用* 执行 `try` 块中的代码。如果失败,完全回滚在 `try` 块中所做的更改,并执行 `catch` 块;`catch` 接收两个参数:任何类型的异常参数(`x`)和错误代码(`n`,整数)。 与许多其他语言不同,在 FunC 的 try-catch 语句中,try 块中所做的更改,特别是局部和全局变量的修改,所有寄存器的更改(即 `c4` 存储寄存器、`c5` 操作/消息寄存器、`c7` 上下文寄存器等)**被丢弃**,如果 try 块中有错误,因此所有合约存储更新和消息发送将被撤销。需要注意的是,一些 TVM 状态参数,如 _codepage_ 和gas计数器不会回滚。这意味着,尤其是,try 块中花费的所有gas将被计入,以及改变gas限制的操作(`accept_message` 和 `set_gas_limit`)的效果将被保留。 -请注意,异常参数可以是任何类型(可能在不同异常情况下不同),因此 funC 无法在编译时预测它。这意味着开发者需要通过将异常参数转换为某种类型来“帮助”编译器(请参见下面的示例 2): +请注意,异常参数可以是任何类型(可能在不同异常情况下不同),因此 FunC 无法在编译时预测它。这意味着开发者需要通过将异常参数转换为某种类型来“帮助”编译器(请参见下面的示例 2): 示例: ```func @@ -323,7 +323,7 @@ try { throw_arg(-1, 100); } catch (x, n) { x.cast_to_int(); - ;; x = -1, n = 100 + // x = -1, n = 100 return x + 1; } ``` @@ -334,7 +334,7 @@ try { throw(100); } catch (_, _) { } -;; x = 0(而非 1) +// x = 0(而非 1) ``` ## 区块语句 diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/stdlib.mdx b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/stdlib.mdx index 337a060e8d..e6cb7c1ff7 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/stdlib.mdx +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/func/stdlib.mdx @@ -24,143 +24,143 @@ toc_max_heading_level: 6 列表可以表示为嵌套的 2 元组。空列表通常表示为 TVM `null` 值(可以通过调用 `null()` 获得)。例如,元组 `(1, (2, (3, null)))` 表示列表 `[1, 2, 3]`。列表的元素可以是不同类型。 #### cons ```func -forall X -> tuple cons(X head, tuple tail) asm "CONS"; +forall X -> tuple cons(X head, tuple tail) pure asm "CONS"; ``` 在 Lisp 类型列表的开头添加一个元素。 #### uncons ```func -forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; +forall X -> (X, tuple) uncons(tuple list) pure asm "UNCONS"; ``` 提取 Lisp 类型列表的头和尾。 #### list_next ```func -forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS"; +forall X -> (tuple, X) list_next(tuple list) pure asm( -> 1 0) "UNCONS"; ``` 提取 Lisp 类型列表的头和尾。可用作 [(非)修改方法](/develop/func/statements#methods-calls)。 ```func () foo(tuple xs) { - (_, int x) = xs.list_next(); ;; 获取第一个元素,`_` 表示不使用尾列表 - int y = xs~list_next(); ;; 弹出第一个元素 - int z = xs~list_next(); ;; 弹出第二个元素 + (_, int x) = xs.list_next(); // 获取第一个元素,`_` 表示不使用尾列表 + int y = xs~list_next(); // 弹出第一个元素 + int z = xs~list_next(); // 弹出第二个元素 } ``` #### car ```func -forall X -> X car(tuple list) asm "CAR"; +forall X -> X car(tuple list) pure asm "CAR"; ``` 返回 Lisp 类型列表的头部。 #### cdr ```func -tuple cdr(tuple list) asm "CDR"; +tuple cdr(tuple list) pure asm "CDR"; ``` 返回 Lisp 类型列表的尾部。 ### 其他元组原语 #### empty_tuple ```func -tuple empty_tuple() asm "NIL"; +tuple empty_tuple() pure asm "NIL"; ``` 创建 0 元素元组。 #### tpush ```func -forall X -> tuple tpush(tuple t, X value) asm "TPUSH"; -forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH"; +forall X -> tuple tpush(tuple t, X value) pure asm "TPUSH"; +forall X -> (tuple, ()) ~tpush(tuple t, X value) pure asm "TPUSH"; ``` 将值 `x` 追加到 `Tuple t = (x1, ..., xn)`,但只有在结果 `Tuple t' = (x1, ..., xn, x)` 不超过 255 个字符时才有效。否则,会抛出类型检查异常。 #### single ```func -forall X -> [X] single(X x) asm "SINGLE"; +forall X -> [X] single(X x) pure asm "SINGLE"; ``` 创建单例,即长度为一的元组。 #### unsingle ```func -forall X -> X unsingle([X] t) asm "UNSINGLE"; +forall X -> X unsingle([X] t) pure asm "UNSINGLE"; ``` 解包单例。 #### pair ```func -forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR"; +forall X, Y -> [X, Y] pair(X x, Y y) pure asm "PAIR"; ``` 创建一对。 #### unpair ```func -forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR"; +forall X, Y -> (X, Y) unpair([X, Y] t) pure asm "UNPAIR"; ``` 解包一对。 #### triple ```func -forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE"; +forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) pure asm "TRIPLE"; ``` 创建三元组。 #### untriple ```func -forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE"; +forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) pure asm "UNTRIPLE"; ``` 解包三元组。 #### tuple4 ```func -forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE"; +forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) pure asm "4 TUPLE"; ``` 创建四元组。 #### untuple4 ```func -forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE"; +forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) pure asm "4 UNTUPLE"; ``` 解包四元组。 #### first ```func -forall X -> X first(tuple t) asm "FIRST"; +forall X -> X first(tuple t) pure asm "FIRST"; ``` 返回元组的第一个元素。 #### second ```func -forall X -> X second(tuple t) asm "SECOND"; +forall X -> X second(tuple t) pure asm "SECOND"; ``` 返回元组的第二个元素。 #### third ```func -forall X -> X third(tuple t) asm "THIRD"; +forall X -> X third(tuple t) pure asm "THIRD"; ``` 返回元组的第三个元素。 #### fourth ```func -forall X -> X fourth(tuple t) asm "3 INDEX"; +forall X -> X fourth(tuple t) pure asm "3 INDEX"; ``` 返回元组的第四个元素。 #### pair_first ```func -forall X, Y -> X pair_first([X, Y] p) asm "FIRST"; +forall X, Y -> X pair_first([X, Y] p) pure asm "FIRST"; ``` 返回一对的第一个元素。 #### pair_second ```func -forall X, Y -> Y pair_second([X, Y] p) asm "SECOND"; +forall X, Y -> Y pair_second([X, Y] p) pure asm "SECOND"; ``` 返回一对的第二个元素。 #### triple_first ```func -forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST"; +forall X, Y, Z -> X triple_first([X, Y, Z] p) pure asm "FIRST"; ``` 返回三元组的第一个元素。 #### triple_second ```func -forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND"; +forall X, Y, Z -> Y triple_second([X, Y, Z] p) pure asm "SECOND"; ``` 返回三元组的第二个元素。 #### triple_third ```func -forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD"; +forall X, Y, Z -> Z triple_third([X, Y, Z] p) pure asm "THIRD"; ``` 返回三元组的第三个元素。 @@ -169,61 +169,61 @@ forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD"; 关于智能合约调用的一些有用信息可以在 [c7 特殊寄存器](/learn/tvm-instructions/tvm-overview#control-registers)中找到。这些原语用于方便地提取数据。 #### now ```func -int now() asm "NOW"; +int now() pure asm "NOW"; ``` 返回当前 Unix 时间作为整数。 #### my_address ```func -slice my_address() asm "MYADDR"; +slice my_address() pure asm "MYADDR"; ``` 以 Slice 形式返回当前智能合约的内部地址,其中包含 `MsgAddressInt`。如果需要,可以进一步使用诸如 `parse_std_addr` 之类的原语进行解析。 #### get_balance ```func -[int, cell] get_balance() asm "BALANCE"; +[int, cell] get_balance() pure asm "BALANCE"; ``` 以 `tuple` 形式返回智能合约的剩余余额,其中包括 `int`(剩余余额,以nanoton计)和 `cell`(一个包含 32 位键的字典,代表“额外代币”的余额)。注意,RAW 原语(如 `send_raw_message`)不会更新此字段。 #### cur_lt ```func -int cur_lt() asm "LTIME"; +int cur_lt() pure asm "LTIME"; ``` 返回当前交易的逻辑时间。 #### block_lt ```func -int block_lt() asm "BLOCKLT"; +int block_lt() pure asm "BLOCKLT"; ``` 返回当前区块的起始逻辑时间。 #### config_param ```func -cell config_param(int x) asm "CONFIGOPTPARAM"; +cell config_param(int x) pure asm "CONFIGOPTPARAM"; ``` 以 `cell` 或 `null` 值的形式返回全局配置参数的值,其中整数索引为 `i`。 ### 哈希 #### cell_hash ```func -int cell_hash(cell c) asm "HASHCU"; +int cell_hash(cell c) pure asm "HASHCU"; ``` 计算`cell c`的 representation hash ,并将其作为一个256位无符号整数`x`返回。用于签名和检查由cell树表示的任意实体的签名。 #### slice_hash ```func -int slice_hash(slice s) asm "HASHSU"; +int slice_hash(slice s) pure asm "HASHSU"; ``` 计算`slice s`的哈希,并将其作为一个256位无符号整数`x`返回。结果与创建一个只包含`s`的数据和引用的普通cell,并通过`cell_hash`计算其哈希的情况相同。 #### string_hash ```func -int string_hash(slice s) asm "SHA256U"; +int string_hash(slice s) pure asm "SHA256U"; ``` 计算`slice s`数据位的sha256。如果`s`的位长度不能被八整除,则抛出一个cell下溢异常。哈希值作为一个256位无符号整数`x`返回。 ### 签名检查 #### check_signature ```func -int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU"; +int check_signature(int hash, slice signature, int public_key) pure asm "CHKSIGNU"; ``` 使用`public_key`(也表示为一个256位无符号整数)检查`hash`(通常作为某些数据的哈希计算得出的256位无符号整数)的Ed25519 `signature`。签名必须包含至少512个数据位;只使用前512位。如果签名有效,结果为`-1`;否则,为`0`。请注意,`CHKSIGNU`创建一个包含哈希的256位切片,并调用`CHKSIGNS`。也就是说,如果`hash`是作为某些数据的哈希计算的,这些数据会被_两次_哈希,第二次哈希发生在`CHKSIGNS`内部。 #### check_data_signature ```func -int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS"; +int check_data_signature(slice data, slice signature, int public_key) pure asm "CHKSIGNS"; ``` 检查`signature`是否是使用`public_key`的`slice data`数据部分的有效Ed25519签名,类似于`check_signature`。如果`data`的位长度不能被八整除,则抛出一个cell下溢异常。Ed25519签名的验证是标准的,使用sha256将`data`简化为实际签名的256位数字。 @@ -231,76 +231,76 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI 下面的原语可能对于计算用户提供数据的存储费用有用。 #### compute_data_size? ```func -(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +(int, int, int, int) compute_data_size?(cell c, int max_cells) pure asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; ``` 返回`(x, y, z, -1)`或`(null, null, null, 0)`。递归地计算以`cell c`为根的DAG中不同cell的数量`x`、数据位`y`和cell引用`z`,有效地返回此DAG使用的总存储量,同时考虑到相等cell的识别。`x`、`y`和`z`的值通过对此DAG进行深度优先遍历来计算,并使用访问过的cell哈希的哈希表来防止已访问cell的重复访问。访问的cell总数`x`不能超过非负的`max_cells`;否则,在访问第`(max_cells + 1)`个cell之前,计算将被中止,并返回零标志以指示失败。如果`c`为`null`,则返回`x = y = z = 0`。 #### slice_compute_data_size? ```func -(int, int, int, int) slice_compute_data_size?(slice s, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +(int, int, int, int) slice_compute_data_size?(slice s, int max_cells) pure asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; ``` 类似于`compute_data_size?`,但接受的是`slice s`而不是`cell`。返回的`x`值不考 虑包含切片`s`本身的cell;然而,`s`的数据位和cell引用在`y`和`z`中要被考虑。 #### compute_data_size ```func -(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE"; +(int, int, int) compute_data_size(cell c, int max_cells) asm "CDATASIZE"; ``` `compute_data_size?`的非静默版本,失败时抛出cell溢出异常(8)。 #### slice_compute_data_size ```func -(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE"; +(int, int, int) slice_compute_data_size(slice s, int max_cells) asm "SDATASIZE"; ``` `slice_compute_data_size?`的非静默版本,失败时抛出cell溢出异常(8)。 ### 持久存储保存和加载 #### get_data ```func -cell get_data() asm "c4 PUSH"; +cell get_data() pure asm "c4 PUSH"; ``` 返回持久化合约存储cell。稍后可以使用切片和构建器原语对其进行解析或修改。 #### set_data ```func -() set_data(cell c) impure asm "c4 POP"; +() set_data(cell c) asm "c4 POP"; ``` 将cell`c`设置为持久化合约数据。您可以使用这个原语更新持久化合约存储。 ### Continuation 原语 #### get_c3 ```func -cont get_c3() impure asm "c3 PUSH"; +cont get_c3() pure asm "c3 PUSH"; ``` 通常`c3`有一个由合约的整个代码初始化的continuation。它用于函数调用。原语返回`c3`的当前值。 #### set_c3 ```func -() set_c3(cont c) impure asm "c3 POP"; +() set_c3(cont c) asm "c3 POP"; ``` 更新`c3`的当前值。通常,它用于实时更新智能合约代码。请注意,在执行此原语之后,当前代码(以及递归函数调用堆栈)不会改变,但任何其他函数调用将使用新代码中的函数。 #### bless ```func -cont bless(slice s) impure asm "BLESS"; +cont bless(slice s) pure asm "BLESS"; ``` 将`slice s`转换为一个简单的普通 continuation `c`,其中`c.code = s`,堆栈和保存列表为空。 ### 与 gas 相关的原语 #### accept_message ```func -() accept_message() impure asm "ACCEPT"; +() accept_message() asm "ACCEPT"; ``` 将当前 gas 限制`gl`设置为其允许的最大值`gm`,并将 gas 信用`gc`重置为零,同时减少`gr`的值`gc`。换句话说,当前智能合约同意购买一些 gas 以完成当前交易。这个动作是处理不携带价值(因此不含 gas )的外部消息所必需的。 有关更多详细信息,请查看[accept_message effects](/develop/smart-contracts/guidelines/accept) #### set_gas_limit ```func -() set_gas_limit(int limit) impure asm "SETGASLIMIT"; +() set_gas_limit(int limit) asm "SETGASLIMIT"; ``` 将当前 gas 限制`gl`设置为`limit`和`gm`的最小值,并将 gas 信用`gc`重置为零。此时,如果消耗的 gas 量(包括当前指令)超过`gl`的结果值,则在设置新 gas 限制之前会抛出(未处理的) gas 不足异常。请注意,带有`limit ≥ 2^63 − 1`参数的`set_gas_limit`等同于`accept_message`。 有关更多详细信息,请查看[accept_message effects](/develop/smart-contracts/guidelines/accept) #### commit ```func -() commit() impure asm "COMMIT"; +() commit() asm "COMMIT"; ``` 提交寄存器`c4`(“持久数据”)和`c5`(“动作”)的当前状态,以便即使稍后抛出异常,当前执行也被视为“成功”,并保存这些值。 #### buy_gas ```func -() buy_gas(int gram) impure asm "BUYGAS"; +() buy_gas(int gram) asm "BUYGAS"; ``` :::caution `BUYGAS`操作码目前尚未实现 @@ -311,17 +311,17 @@ cont bless(slice s) impure asm "BLESS"; ### 动作原语 #### raw_reserve ```func -() raw_reserve(int amount, int mode) impure asm "RAWRESERVE"; +() raw_reserve(int amount, int mode) asm "RAWRESERVE"; ``` 创建一个输出动作,该动作将准确地预留`amount` nanoton 币(如果`mode = 0`),最多`amount` nanoton 币(如果`mode = 2`),或除`amount` nanoton 币以外的所有 nanoton 币(如果`mode = 1`或`mode = 3`)从账户的剩余余额中。它大致等同于创建一个携带`amount` nanoton 币(或`b − amount` nanoton 币,其中`b`是剩余余额)的出站消息发送给自己,这样随后的输出动作就不会花费超过剩余部分的金额。`mode`中的+2位意味着外部动作在无法预留指定金额时不会失败;相反,将预留所有剩余余额。`mode`中的+8位意味着`amount <- -amount`在进行任何进一步的动作之前。`mode`中的+4位意味着在进行任何其他检查和动作之前,`amount`会增加当前账户的原始余额(在 Compute Phase 之前),包括所有额外代币。目前,`amount`必须是非负整数,`mode`必须在`0..15`范围内。 #### raw_reserve_extra ```func -() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX"; +() raw_reserve_extra(int amount, cell extra_amount, int mode) asm "RAWRESERVEX"; ``` 类似于`raw_reserve`,但还接受一个由`cell`或`null`表示的额外代币字典`extra_amount`。这样,除了Toncoin以外的其他代币也可以被预留。 #### send_raw_message ```func -() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG"; +() send_raw_message(cell msg, int mode) asm "SENDRAWMSG"; ``` 发送包含在`msg`中的原始消息,它应该包含一个正确序列化的消息对象X,唯一的例外是源地址可以有一个虚拟值`addr_none`(自动替换为当前智能合约地址),以及`ihr_fee`、`fwd_fee`、`created_lt`和`created_at`字段可以有任意值(在当前交易的 Action Phase 期间用正确的值重写)。整数参数`mode`包含标志。 @@ -346,7 +346,7 @@ cont bless(slice s) impure asm "BLESS"; #### set_code ```func -() set_code(cell new_code) impure asm "SETCODE"; +() set_code(cell new_code) asm "SETCODE"; ``` 创建一个输出动作,该动作将更改此智能合约的代码为cell`new_code`给出的代码。请注意,此更改仅在当前智能合约的当前运行成功终止后才生效。(参见[set_c3](/develop/func/stdlib#set_c3.)) @@ -359,41 +359,41 @@ cont bless(slice s) impure asm "BLESS"; ::: #### random ```func -int random() impure asm "RANDU256"; +int random() asm "RANDU256"; ``` 生成一个新的伪随机无符号256位整数`x`。算法如下:如果`r`是旧的随机种子值,被视为一个32字节的数组(通过构造一个无符号256位整数的大端表示),那么计算其`sha512(r)`;这个哈希的前32字节被存储为随机种子的新值`r'`,剩余的32字节作为下一个随机值`x`返回。 #### rand ```func -int rand(int range) impure asm "RAND"; +int rand(int range) asm "RAND"; ``` 在范围`0..range−1`(或`range..−1`,如果`range < 0`)内生成一个新的伪随机整数`z`。更准确地说,生成一个无符号随机值`x`,如`random`中一样;然后计算`z := x * range / 2^256`。 #### get_seed ```func -int get_seed() impure asm "RANDSEED"; +int get_seed() pure asm "RANDSEED"; ``` 以一个无符号的256位整数返回当前随机种子。 #### set_seed ```func -int set_seed(int seed) impure asm "SETRAND"; +int set_seed(int seed) asm "SETRAND"; ``` 将随机种子设置为一个无符号的256位`seed`。 #### randomize ```func -() randomize(int x) impure asm "ADDRAND"; +() randomize(int x) asm "ADDRAND"; ``` 通过将随机种子设置为两个32字节字符串的串联的sha256来将一个无符号的256位整数`x`混合到随机种子`r`中,这两个32字节字符串:第一个包含旧种子`r`的大端表示,第二个包含`x`的大端表示。 #### randomize_lt ```func -() randomize_lt() impure asm "LTIME" "ADDRAND"; +() randomize_lt() asm "LTIME" "ADDRAND"; ``` 相当于`randomize(cur_lt());`。 ### 地址操作原语 下面列出的地址操作原语根据以下TL-B方案序列化和反序列化值。 -```func +```tlb addr_none$00 = MsgAddressExt; addr_extern$01 len:(## 8) external_address:(bits len) @@ -428,23 +428,23 @@ ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt #### load_msg_addr ```func -(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR"; +(slice, slice) load_msg_addr(slice s) pure asm( -> 1 0) "LDMSGADDR"; ``` 从 `slice s` 加载唯一有效的 `MsgAddress` 前缀,并返回此前缀 `s'` 及 `s` 的其余部分 `s''` 作为切片。 #### parse_addr ```func -tuple parse_addr(slice s) asm "PARSEMSGADDR"; +tuple parse_addr(slice s) pure asm "PARSEMSGADDR"; ``` 将包含有效 `MsgAddress` 的 `slice s` 分解为 `tuple t`,并包含此 `MsgAddress` 的独立字段。如果 `s` 不是有效的 `MsgAddress`,则抛出 cell 反序列化异常。 #### parse_std_addr ```func -(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR"; +(int, int) parse_std_addr(slice s) pure asm "REWRITESTDADDR"; ``` 解析包含有效 `MsgAddressInt`(通常为 `msg_addr_std`)的切片 `s`,将重写 `anycast`(如果存在)应用到地址相同长度前缀,并返回工作链和256位地址作为整数。如果地址不是256位,或者 `s` 不是 `MsgAddressInt` 的有效序列化,抛出cell `deserialization` 异常。 #### parse_var_addr ```func -(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR"; +(int, slice) parse_var_addr(slice s) pure asm "REWRITEVARADDR"; ``` `parse_std_addr` 的变体,即使地址不是正好256位长(由 `msg_addr_var` 表示),也以切片 `s` 返回(重写后的)地址。 @@ -452,7 +452,7 @@ tuple parse_addr(slice s) asm "PARSEMSGADDR"; 目前,只有一个函数可用。 #### dump_stack ```func -() dump_stack() impure asm "DUMPSTK"; +() dump_stack() asm "DUMPSTK"; ``` 转储堆栈(最多前255个值)并显示总堆栈深度。 @@ -464,137 +464,135 @@ tuple parse_addr(slice s) asm "PARSEMSGADDR"; 除非另有说明,加载和预加载原语从切片的前缀读取数据。 #### begin_parse ```func -slice begin_parse(cell c) asm "CTOS"; +slice begin_parse(cell c) pure asm "CTOS"; ``` 将 `cell` 转换为 `slice`。注意,`c` 必须是普通cell或特殊cell(见 [TVM.pdf](https://ton.org/tvm.pdf), 3.1.2),自动加载以产生普通cell `c'`,然后转换为 `slice`。 #### end_parse ```func -() end_parse(slice s) impure asm "ENDS"; +() end_parse(slice s) asm "ENDS"; ``` 检查 `s` 是否为空。如果不是,则抛出异常。 #### load_ref ```func -(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; +(slice, cell) load_ref(slice s) pure asm( -> 1 0) "LDREF"; ``` 从切片中加载第一个引用。 #### preload_ref ```func -cell preload_ref(slice s) asm "PLDREF"; +cell preload_ref(slice s) pure asm "PLDREF"; ``` 从切片中预加载第一个引用。 #### load_int ```func -;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +(slice, int) ~load_int(slice s, int len) pure builtin; ``` 从切片中加载一个有符号的 `len` 位整数。 #### load_uint ```func -;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; +(slice, int) ~load_uint(slice s, int len) pure builtin; ``` 从切片中加载一个无符号的 `len` 位整数。 #### preload_int ```func -;; int preload_int(slice s, int len) asm "PLDIX"; +int preload_int(slice s, int len) pure builtin; ``` 从切片中预加载一个有符号的 `len` 位整数。 #### preload_uint ```func -;; int preload_uint(slice s, int len) asm "PLDUX"; +int preload_uint(slice s, int len) pure builtin; ``` 从切片中预加载一个无符号的 `len` 位整数。 #### load_bits ```func -;; (slice, slice) load_bits(slice s, int len) asm(s len -> - - 1 0) "LDSLICEX"; +(slice, slice) load_bits(slice s, int len) pure builtin; ``` 从切片 `s` 中加载前 `0 ≤ len ≤ 1023` 位到一个单独的切片 `s''`。 #### preload_bits ```func -;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; +slice preload_bits(slice s, int len) pure builtin; ``` 从切片 `s` 中预加载前 `0 ≤ len ≤ 1023` 位到一个单独的切片 `s''`。 #### load_coins ```func -(slice, int) load_coins(slice s) asm( -> 1 0) "LDGRAMS"; +(slice, int) load_coins(slice s) pure asm( -> 1 0) "LDGRAMS"; ``` 加载序列化的 Toncoins 数量(任何最高为 `2^120 - 1` 的无符号整数)。 #### skip_bits ```func -slice skip_bits(slice s, int len) asm "SDSKIPFIRST"; -(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST"; +slice skip_bits(slice s, int len) pure asm "SDSKIPFIRST"; +(slice, ()) ~skip_bits(slice s, int len) pure asm "SDSKIPFIRST"; ``` 返回 `s` 的前 `0 ≤ len ≤ 1023` 位以外的所有值。 #### first_bits ```func -slice first_bits(slice s, int len) asm "SDCUTFIRST"; +slice first_bits(slice s, int len) pure asm "SDCUTFIRST"; ``` 返回 `s` 的前 `0 ≤ len ≤ 1023` 位。 #### skip_last_bits ```func -slice skip_last_bits(slice s, int len) asm "SDSKIPLAST"; -(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST"; +slice skip_last_bits(slice s, int len) pure asm "SDSKIPLAST"; +(slice, ()) ~skip_last_bits(slice s, int len) pure asm "SDSKIPLAST"; ``` 返回 `s` 中除最后 `0 ≤ len ≤ 1023` 位之外的所有值。 #### slice_last ```func -slice slice_last(slice s, int len) asm "SDCUTLAST"; +slice slice_last(slice s, int len) pure asm "SDCUTLAST"; ``` 返回 `s` 的最后 `0 ≤ len ≤ 1023` 位。 #### load_dict ```func -(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT"; +(slice, cell) load_dict(slice s) pure asm( -> 1 0) "LDDICT"; ``` 从切片 `s` 中加载字典 `D`。可应用于字典或任意 `Maybe ^Y` 类型的值(如果使用 `nothing` 构造器,则返回 `null`)。 #### preload_dict ```func -cell preload_dict(slice s) asm "PLDDICT"; +cell preload_dict(slice s) pure asm "PLDDICT"; ``` 从切片 `s` 中预加载字典 `D`。 #### skip_dict ```func -slice skip_dict(slice s) asm "SKIPDICT"; +slice skip_dict(slice s) pure asm "SKIPDICT"; ``` 像 `load_dict` 一样加载字典,但只返回切片的其余部分。 ### 切片大小原语 #### slice_refs ```func -int slice_refs(slice s) asm "SREFS"; +int slice_refs(slice s) pure asm "SREFS"; ``` 返回切片 `s` 中的引用数量。 #### slice_bits ```func -int slice_bits(slice s) asm "SBITS"; +int slice_bits(slice s) pure asm "SBITS"; ``` 返回切片 `s` 中的数据位数。 #### slice_bits_refs ```func -(int, int) slice_bits_refs(slice s) asm "SBITREFS"; +(int, int) slice_bits_refs(slice s) pure asm "SBITREFS"; ``` 返回 `s` 中的数据位数和引用数量。 #### slice_empty? ```func -int slice_empty?(slice s) asm "SEMPTY"; +int slice_empty?(slice s) pure asm "SEMPTY"; ``` 检查切片 `s` 是否为空(即,不包含数据位和 cell 引用)。 #### slice_data_empty? ```func -int slice_data_empty?(slice s) asm "SDEMPTY"; +int slice_data_empty?(slice s) pure asm "SDEMPTY"; ``` 检查切片 `s` 是否没有数据位。 #### slice_refs_empty? ```func -int slice_refs_empty?(slice s) asm "SREMPTY"; +int slice_refs_empty?(slice s) pure asm "SREMPTY"; ``` 检查切片 `s` 是否没有引用。 #### slice_depth ```func -int slice_depth(slice s) asm "SDEPTH"; +int slice_depth(slice s) pure asm "SDEPTH"; ``` 返回切片 `s` 的深度。如果 `s` 没有引用,则返回 `0`;否则,返回值是 `s` 中引用的 cell 的深度最大值加一。 @@ -604,41 +602,41 @@ int slice_depth(slice s) asm "SDEPTH"; 下面列出的所有原语首先检查构建器中是否有足够的空间,然后是被序列化值的范围。 #### begin_cell ```func -builder begin_cell() asm "NEWC"; +builder begin_cell() pure asm "NEWC"; ``` 创建一个新的空 `builder`。 #### end_cell ```func -cell end_cell(builder b) asm "ENDC"; +cell end_cell(builder b) pure asm "ENDC"; ``` 将 `builder` 转换为普通的 `cell`。 #### store_ref ```func -builder store_ref(builder b, cell c) asm(c b) "STREF"; +builder store_ref(builder b, cell c) pure asm(c b) "STREF"; ``` 将对 cell `c` 的引用存储到构建器 `b` 中。 #### store_uint ```func -builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; +builder store_uint(builder b, int x, int len) pure builtin; ``` 将无符号的 `len` 位整数 `x` 存储到 `b` 中,`0 ≤ len ≤ 256`。 #### store_int ```func -builder store_int(builder b, int x, int len) asm(x b len) "STIX"; +builder store_int(builder b, int x, int len) pure builtin; ``` 将有符号的 `len` 位整数 `x` 存储到 `b` 中,`0 ≤ len ≤ 257`。 #### store_slice ```func -builder store_slice(builder b, slice s) asm "STSLICER"; +builder store_slice(builder b, slice s) pure asm "STSLICER"; ``` 将切片 `s` 存储到构建器 `b` 中。 #### store_grams ```func -builder store_grams(builder b, int x) asm "STGRAMS"; +builder store_grams(builder b, int x) pure asm "STGRAMS"; ``` #### store_coins ```func -builder store_coins(builder b, int x) asm "STGRAMS"; +builder store_coins(builder b, int x) pure asm "STGRAMS"; ``` 将范围 `0..2^120 − 1` 内的整数 `x` 存储(序列化)到构建器 `b` 中。`x` 的序列化包含一个4位无符号大端整数 `l`,它是最小的整数 `l ≥ 0`,使得 `x < 2^8l`,后跟 `8l` 位无符号大端表示的 `x`。如果 `x` 不属于支持范围,则抛出范围检查异常。 @@ -646,43 +644,43 @@ builder store_coins(builder b, int x) asm "STGRAMS"; #### store_dict ```func -builder store_dict(builder b, cell c) asm(c b) "STDICT"; +builder store_dict(builder b, cell c) pure asm(c b) "STDICT"; ``` 将由 cell `c` 或 `null` 表示的字典 `D` 存储到构建器 `b` 中。换句话说,如果 `c` 不是 `null`,则存储1位和对 `c` 的引用;否则存储0位。 #### store_maybe_ref ```func -builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; +builder store_maybe_ref(builder b, cell c) pure asm(c b) "STOPTREF"; ``` 等同于 `store_dict`。 ### 构建器大小原语 #### builder_refs ```func -int builder_refs(builder b) asm "BREFS"; +int builder_refs(builder b) pure asm "BREFS"; ``` 返回已经存储在构建器 `b` 中的 cell 引用数量。 #### builder_bits ```func -int builder_bits(builder b) asm "BBITS"; +int builder_bits(builder b) pure asm "BBITS"; ``` 返回已经存储在构建器 `b` 中的数据位数。 #### builder_depth ```func -int builder_depth(builder b) asm "BDEPTH"; +int builder_depth(builder b) pure asm "BDEPTH"; ``` 返回构建器 `b` 的深度。如果 `b` 中没有存储任何 cell 引用,则返回 `0`;否则,返回值是从 `b` 中引用的 cell 的最大深度加一。 ## Cell原语 #### cell_depth ```func -int cell_depth(cell c) asm "CDEPTH"; +int cell_depth(cell c) pure asm "CDEPTH"; ``` 返回 cell `c` 的深度。如果 `c` 没有引用,则返回 `0`;否则,返回值是从 `c` 中引用的 cell 的最大深度加一。如果 `c` 是 `null`而不是 cell ,则返回零。 #### cell_null? ```func -int cell_null?(cell c) asm "ISNULL"; +int cell_null?(cell c) pure asm "ISNULL"; ``` -检查 `c` 是否为 `null`。通常 `null`-cell 表示一个空字典。FunC 也有多态的 `null?` 内置函数。(见 [内置函数](/develop/func/builtins#other-primitives)。) +检查 `c` 是否为 `null`。通常 `null`-cell 表示一个空字典。FunC 也有多态的 `null?` 内置函数。 ## 字典原语 @@ -713,106 +711,106 @@ int cell_null?(cell c) asm "ISNULL"; #### dict_set ```func -cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; -cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; -cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; -(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; -(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; -(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; +cell udict_set(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTUSET"; +cell idict_set(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTISET"; +cell dict_set(cell dict, int key_len, slice index, slice value) pure asm(value index dict key_len) "DICTSET"; +(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTUSET"; +(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTISET"; +(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) pure asm(value index dict key_len) "DICTSET"; ``` 在字典 `dict` 中设置与 `key_len` 位键 `index` 关联的值 `value`(一个切片),并返回结果字典。 #### dict_set_ref ```func -cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; -cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; -(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; -(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; +cell idict_set_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTISETREF"; +cell udict_set_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTUSETREF"; +(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTISETREF"; +(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTUSETREF"; ``` 类似于 `dict_set`,但值设置为对 cell `value` 的引用。 #### dict_get? ```func -(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT"; -(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT"; +(slice, int) idict_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT"; +(slice, int) udict_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT"; ``` 在字典 `dict` 中查找 `key_len` 位键 `index`。成功时,返回找到的值作为切片以及表示成功的 `-1` 标志位。如果失败,则返回 `(null, 0)`。 #### dict_get_ref? ```func -(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF"; -(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF"; +(cell, int) idict_get_ref?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIGETREF"; +(cell, int) udict_get_ref?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUGETREF"; ``` 类似于 `dict_get?`,但返回找到的值的第一个引用。 #### dict_get_ref ```func -cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF"; +cell idict_get_ref(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIGETOPTREF"; ``` `dict_get_ref?` 的变体,如果键 `index` 不在字典 `dict` 中,则返回 `null` 而不是值。 #### dict_set_get_ref ```func -(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF"; -(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF"; +(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTISETGETOPTREF"; +(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) pure asm(value index dict key_len) "DICTUSETGETOPTREF"; ``` 将与 `index` 关联的值设置为 `value`(如果 `value` 为 `null`,则删除键),并返回旧值(如果值不存在,则为 `null`)。 #### dict_delete? ```func -(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL"; -(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL"; +(cell, int) idict_delete?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIDEL"; +(cell, int) udict_delete?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUDEL"; ``` 从字典 `dict` 中删除 `key_len` 位键 `index`。如果键存在,则返回修改后的字典 `dict'` 和成功标志位 `−1`。否则,返回原始字典 `dict` 和 `0`。 #### dict_delete_get? ```func (cell, slice, int) idict_delete -_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; -(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; -(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; -(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) pure asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; ``` 从字典 `dict` 中删除 `key_len` 位键 `index`。如果键存在,则返回修改后的字典 `dict'`、与键 k 关联的原始值 `x`(由一个切片表示),以及成功标志位 `−1`。否则,返回 `(dict, null, 0)`。 #### dict_add? ```func -(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD"; -(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD"; +(cell, int) udict_add?(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTUADD"; +(cell, int) idict_add?(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTIADD"; ``` `dict_set` 的 `add` 对应项,将字典 `dict` 中与键 `index` 关联的值设置为 `value`,但仅当它尚未出现在 `D` 中时。返回修改后的字典和 `-1` 标志位或 `(dict, 0)`。 #### dict_replace? ```func -(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE"; -(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE"; +(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTUREPLACE"; +(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) pure asm(value index dict key_len) "DICTIREPLACE"; ``` 类似于 `dict_set` 的 `replace` 操作,但只有在键 `index` 已经出现在 `dict` 中时才将字典 `dict` 中键 `index` 的值设置为 `value`。返回修改后的字典和 `-1` 标志位或 `(dict, 0)`。 ### 构建器对应项 下面的原语接受新值作为构建器而不是切片,如果需要从堆栈中计算的几个组件序列化值,这通常更方便。其效果大致相当于将 b 转换为切片并执行上面列出的相应原语。 #### dict_set_builder ```func -cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; -cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; -cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; -(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; -(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; -(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; +cell udict_set_builder(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTUSETB"; +cell idict_set_builder(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTISETB"; +cell dict_set_builder(cell dict, int key_len, slice index, builder value) pure asm(value index dict key_len) "DICTSETB"; +(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTISETB"; +(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTUSETB"; +(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) pure asm(value index dict key_len) "DICTSETB"; ``` 类似于 `dict_set`,但接受构建器。 #### dict_add_builder? ```func -(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB"; -(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB"; +(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTUADDB"; +(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTIADDB"; ``` 类似于 `dict_add?`,但接受构建器。 #### dict_replace_builder? ```func -(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB"; -(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB"; +(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTUREPLACEB"; +(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) pure asm(value index dict key_len) "DICTIREPLACEB"; ``` 类似于 `dict_replace?`,但接受构建器。 #### dict_delete_get_min ```func -(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; -(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; -(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; -(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; ``` 计算字典 `dict` 中的最小键 `k`,将其移除,并返回 `(dict', k, x, -1)`,其中 `dict'` 是修改后的 `dict`,`x` 是与 `k` 关联的值。如果字典为空,则返回 `(dict, null, null, 0)`。 @@ -820,71 +818,71 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va #### dict_delete_get_max ```func -(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; -(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; -(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; -(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) pure asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; ``` 计算字典 `dict` 中的最大键 `k`,将其移除,并返回 `(dict', k, x, -1)`,其中 `dict'` 是修改后的 `dict`,`x` 是与 `k` 关联的值。如果字典为空,则返回 `(dict, null, null, 0)`。 #### dict_get_min? ```func -(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_min?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_min?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; ``` 计算字典 `dict` 中的最小键 `k` 及其关联值 `x`,并返回 `(k, x, -1)`。如果字典为空,则返回 `(null, null, 0)`。 #### dict_get_max? ```func -(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_max?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_max?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; ``` 计算字典 `dict` 中的最大键 `k` 及其关联值 `x`,并返回 `(k, x, -1)`。如果字典为空,则返回 `(null, null, 0)`。 #### dict_get_min_ref? ```func -(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; -(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) udict_get_min_ref?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_min_ref?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; ``` 类似于 `dict_get_min?`,但返回值中唯一的引用作为引用。 #### dict_get_max_ref? ```func -(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; -(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; +(int, cell, int) udict_get_max_ref?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_max_ref?(cell dict, int key_len) pure asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; ``` 类似于 `dict_get_max?`,但返回值中唯一的引用作为引用。 #### dict_get_next? ```func -(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; ``` 计算字典 `dict` 中大于 `pivot` 的最小键 `k`;返回 `k`、关联值和表示成功的标志位。如果字典为空,则返回 `(null, null, 0)`。 #### dict_get_nexteq? ```func -(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; ``` 类似于 `dict_get_next?`,但计算大于或等于 `pivot` 的最小键 `k`。 #### dict_get_prev? ```func -(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; ``` 类似于 `dict_get_next?`,但计算小于 `pivot` 的最大键 `k`。 #### dict_get_preveq? ```func -(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; -(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) pure asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; ``` 类似于 `dict_get_prev?`,但计算小于或等于 `pivot` 的最大键 `k`。 #### new_dict ```func -cell new_dict() asm "NEWDICT"; +cell new_dict() pure asm "NEWDICT"; ``` 创建一个空字典,实际上是一个 `null` 值。`null()` 的特例。 #### dict_empty? ```func -int dict_empty?(cell c) asm "DICTEMPTY"; +int dict_empty?(cell c) pure asm "DICTEMPTY"; ``` 检查字典是否为空。等同于 `cell_null?`。 @@ -893,50 +891,50 @@ TVM 还支持具有非固定长度键的字典,这些键形成前缀码(即 #### pfxdict_get? ```func -(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2"; +(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) pure asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2"; ``` 返回 `(s', x, s'', -1)` 或 `(null, null, s, 0)`。在前缀码字典 `dict` 中查找切片 `key` 的唯一前缀。如果找到,返回 `s` 的前缀作为 `s'` 和相应的值(也是切片)作为 `x`。`s` 的剩余部分作为切片 `s''` 返回。如果 `s` 的任何前缀都不是前缀码字典 `dict` 中的键,则返回未更改的 `s` 和零标志位以表示失败。 #### pfxdict_set? ```func -(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET"; +(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) pure asm(value key dict key_len) "PFXDICTSET"; ``` 类似于 `dict_set`,但如果键是字典中另一个键的前缀,则可能失败。表示成功,返回一个标志位。 #### pfxdict_delete? ```func -(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL"; +(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) pure asm(key dict key_len) "PFXDICTDEL"; ``` 类似于 `dict_delete?`。 ## 特殊原语 #### null ```func -forall X -> X null() asm "PUSHNULL"; +forall X -> X null() pure asm "PUSHNULL"; ``` 通过 TVM 类型 `Null`,FunC 表示某些原子类型的值的缺失。因此 `null` 实际上可以具有任何原子类型。 #### ~impure_touch ```func -forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP"; +forall X -> (X, ()) ~impure_touch(X x) asm "NOP"; ``` 标记一个变量为已使用,以便即使它不是非纯的(impure),产生它的代码也不会被删除。(参见 [非纯修饰符](/develop/func/functions#impure-specifier)) ## 其他原语 #### min ```func -int min(int x, int y) asm "MIN"; +int min(int x, int y) pure asm "MIN"; ``` 计算两个整数 `x` 和 `y` 的最小值。 #### max ```func -int max(int x, int y) asm "MAX"; +int max(int x, int y) pure asm "MAX"; ``` 计算两个整数 `x` 和 `y` 的最大值。 #### minmax ```func -(int, int) minmax(int x, int y) asm "MINMAX"; +(int, int) minmax(int x, int y) pure asm "MINMAX"; ``` 对两个整数进行排序。 #### abs ```func -int abs(int x) asm "ABS"; +int abs(int x) pure asm "ABS"; ``` 计算整数 `x` 的绝对值。 diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/fees-low-level.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/fees-low-level.md index 8fe682b054..69f6ad55b5 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/fees-low-level.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/fees-low-level.md @@ -51,7 +51,7 @@ transaction_fee = storage_fees ## FunC 构造的 gas 费用 -FunC中使用的几乎所有函数都在[stdlib.func](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc)中定义,它将FunC函数映射到Fift汇编指令。反过来,Fift汇编指令在[asm.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/fift/lib/Asm.fif)中映射到位序列指令。因此,如果你想了解指令调用的确切成本,你需要在`stdlib.func`中找到`asm`表示,然后在`asm.fif`中找到位序列并计算指令长度(以位为单位)。 +FunC中使用的几乎所有函数都在[stdlib.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc)中定义,它将FunC函数映射到Fift汇编指令。反过来,Fift汇编指令在[asm.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/fift/lib/Asm.fif)中映射到位序列指令。因此,如果你想了解指令调用的确切成本,你需要在`stdlib.func`中找到`asm`表示,然后在`asm.fif`中找到位序列并计算指令长度(以位为单位)。 然而,通常,与位长度相关的费用与cell解析和创建以及跳转和执行指令数量的费用相比是次要的。 @@ -61,7 +61,7 @@ FunC中使用的几乎所有函数都在[stdlib.func](https://github.com/ton-blo 一个关于如何通过适当的cell工作显著降低gas成本的示例。 假设你想在出站消息中添加一些编码的有效负载。直接实现将如下: -```cpp +```func slice payload_encoding(int a, int b, int c) { return begin_cell().store_uint(a,8) @@ -70,15 +70,15 @@ slice payload_encoding(int a, int b, int c) { .end_cell().begin_parse(); } -() send_message(slice destination) impure { +() send_message(slice destination) { slice payload = payload_encoding(1, 7, 12); var msg = begin_cell() .store_uint(0x18, 6) .store_slice(destination) .store_coins(0) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - .store_uint(0x33bbff77, 32) ;; op-code (see smart-contract guidelines) - .store_uint(cur_lt(), 64) ;; query_id (see smart-contract guidelines) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // default message headers (see sending messages page) + .store_uint(0x33bbff77, 32) // op-code (see smart-contract guidelines) + .store_uint(cur_lt(), 64) // query_id (see smart-contract guidelines) .store_slice(payload) .end_cell(); send_raw_message(msg, 64); @@ -87,8 +87,8 @@ slice payload_encoding(int a, int b, int c) { 这段代码的问题是什么?`payload_encoding`为了生成切片位字符串,首先通过`end_cell()`创建一个cell(+500 gas单位)。然后解析它`begin_parse()`(+100 gas单位)。通过改变一些常用类型,可以不使用这些不必要的操作来重写相同的代码: -```cpp -;; 我们为stdlib中不存在的函数添加asm,该函数将一个构建器存储到另一个构建器中 +```func +// 我们为stdlib中不存在的函数添加asm,该函数将一个构建器存储到另一个构建器中 builder store_builder(builder to, builder what) asm(what to) "STB"; builder payload_encoding(int a, int b, int c) { @@ -98,15 +98,15 @@ builder payload_encoding(int a, int b, int c) { .store_uint(c,8); } -() send_message(slice destination) impure { +() send_message(slice destination) { builder payload = payload_encoding(1, 7, 12); var msg = begin_cell() .store_uint(0x18, 6) .store_slice(destination) .store_coins(0) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息头(见发送消息页面) - .store_uint(0x33bbff77, 32) ;; 操作码(见智能合约指南) - .store_uint(cur_lt(), 64) ;; query_id(见智能合约指南) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息头(见发送消息页面) + .store_uint(0x33bbff77, 32) // 操作码(见智能合约指南) + .store_uint(cur_lt(), 64) // query_id(见智能合约指南) .store_builder(payload) .end_cell(); send_raw_message(msg, 64); @@ -125,7 +125,7 @@ TON中的字典是作为cell的树(更准确地说是DAG)被引入的。这 ### 堆栈操作 注意FunC在底层操作堆栈条目。这意味着代码: -```cpp +```func (int a, int b, int c) = some_f(); return (c, b, a); ``` diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/step-by-step.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/step-by-step.md index d8a84c923e..8ab59bafb5 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/step-by-step.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/step-by-step.md @@ -81,7 +81,7 @@ B) "用户友好"形式,首先生成: :::info 请注意,在此及以后的代码、注释和/或文档中可能包含“gram”、“nanogram”等参数、方法和定义。这是原始TON代码的遗留问题,由Telegram开发。Gram加密货币从未发行。TON的货币是Toncoin,TON测试网的货币是Test Toncoin。 ::: -```cpp +``` got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2075):BFE876CE2085274FEDAF1BD80F3ACE50F42B5A027DF230AD66DCED1F09FB39A7:522C027A721FABCB32574E3A809ABFBEE6A71DE929C1FA2B1CD0DDECF3056505 account state is (account addr:(addr_std @@ -141,7 +141,7 @@ last transaction lt = 2310000001 hash = 73F89C6F8910F598AD84504A777E5945C798AC8C 考虑文件`new-wallet.fif`(通常位于构建目录相对的`crypto/smartcont/new-wallet.fif`),其中包含一个简单钱包智能合约的源代码: -```cpp +```fift #!/usr/bin/env fift -s "TonUtil.fif" include "Asm.fif" include @@ -228,7 +228,7 @@ $ fift -s new-wallet.fif 0 my_wallet_name 如果一切顺利,您将看到类似这样的内容: -```cpp +``` Creating new wallet in workchain 0 Saved new private key to file my_wallet_name.pk StateInit: x{34_} @@ -295,7 +295,7 @@ B5EE9C724104030100000000E50002CF88005DD369FA9E0EF93644650186AEC7BF3DB5616835841A 生成正确的值 39445 = 0x9A15: -```cpp +``` got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2240):18E6DA7707191E76C71EABBC5277650666B7E2CFA2AEF2CE607EAFE8657A3820:4EFA2540C5D1E4A1BA2B529EE0B65415DF46BFFBD27A8EB74C4C0E17770D03B1 creating VM starting VM to run method `seqno` (85143) of smart contract -1:FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 @@ -306,7 +306,7 @@ result: [ 39445 ] 接下来,您创建一个外部消息给测试赠予者,要求它发送另一个消息给您的(未初始化的)智能合约,携带指定数量的测试Toncoin。有一个特殊的Fift脚本用于生成这个外部消息,位于`crypto/smartcont/testgiver.fif`: -```cpp +```fift #!/usr/bin/env fift -s "TonUtil.fif" include @@ -370,7 +370,7 @@ $ fift -s testgiver.fif 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb 0x9A15 外部消息被序列化并保存到文件`wallet-query.boc`中。过程中生成了一些输出: -```cpp +``` Test giver address = -1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260 kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny Requesting GR$6.666 to account 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb = 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 seqno=0x9a15 bounce=0 @@ -407,7 +407,7 @@ B5EE9C7241040201000000006600014F89FF02ACEEB6F264BCBAC5CE85B372D8616CA2B4B9A5E3EC (如果您忘记输入`last`,您可能会看到测试赠予者智能合约的未更改状态。)结果输出将是: -```cpp +``` got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2240):18E6DA7707191E76C71EABBC5277650666B7E2CFA2AEF2CE607EAFE8657A3820:4EFA2540C5D1E4A1BA2B529EE0B65415DF46BFFBD27A8EB74C4C0E17770D03B1 account state is (account addr:(addr_std @@ -448,7 +448,7 @@ x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C000 现在我们可以检查我们新智能合约的状态: -```cpp +``` > getaccount 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb 或 > getaccount 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 @@ -456,7 +456,7 @@ x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C000 现在我们看到: -```cpp +``` got account state for 0:2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2 with respect to blocks (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB and (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB account state is (account addr:(addr_std @@ -483,7 +483,7 @@ x{CFF60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D02025BC2E4A0 现在,您终于可以上传包含新智能合约的代码和数据的`StateInit`外部消息了: -```cpp +``` > sendfile my_wallet_name-query.boc ... external message status is 1 > last @@ -534,7 +534,7 @@ x{CFF60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D020680F0C2E4 以下是您如何使用这个智能合约的示例,提供在文件`crypto/smartcont/wallet.fif`中: -```cpp +```fift #!/usr/bin/env fift -s "TonUtil.fif" include diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/subresolvers.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/subresolvers.md index 113cca3344..17ec1a77cc 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/subresolvers.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/howto/subresolvers.md @@ -28,30 +28,30 @@ TON DNS 是一个强大的工具。它不仅允许将 TON 网站/存储包分配 部分重复部分已省略。 ```func -(int, cell) dnsresolve(slice subdomain, int category) method_id { +get (int, cell) dnsresolve(slice subdomain, int category) { int subdomain_bits = slice_bits(subdomain); throw_unless(70, (subdomain_bits % 8) == 0); - int starts_with_zero_byte = subdomain.preload_int(8) == 0; ;; 假设 'subdomain' 不为空 + int starts_with_zero_byte = subdomain.preload_int(8) == 0; // 假设 'subdomain' 不为空 if (starts_with_zero_byte) { subdomain~load_uint(8); - if (subdomain.slice_bits() == 0) { ;; 当前合约没有自己的 DNS 记录 + if (subdomain.slice_bits() == 0) { // 当前合约没有自己的 DNS 记录 return (8, null()); } } - ;; 我们正在加载某个子域名 - ;; 支持的子域名是 "ton\\0", "me\\0t\\0" 和 "address\\0" + // 我们正在加载某个子域名 + // 支持的子域名是 "ton\\0", "me\\0t\\0" 和 "address\\0" slice subdomain_sfx = null(); builder domain_nft_address = null(); if (subdomain.starts_with("746F6E00"s)) { - ;; 我们正在解析 - ;; "ton" \\0 \\0 [subdomain_sfx] + // 我们正在解析 + // "ton" \\0 \\0 [subdomain_sfx] subdomain~skip_bits(32); - ;; 读取域名 + // 读取域名 subdomain_sfx = subdomain; while (subdomain_sfx~load_uint(8)) { } @@ -74,17 +74,17 @@ _bits(subdomain_sfx)); } if (slice_empty?(subdomain_sfx)) { - ;; 解析域名的示例: - ;; [初始,此合约不可访问] "ton\\0resolve-contract\\0ton\\0ratelance\\0" - ;; [此合约可以访问] "ton\\0ratelance\\0" - ;; subdomain "ratelance" - ;; subdomain_sfx "" + // 解析域名的示例: + // [初始,此合约不可访问] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + // [此合约可以访问] "ton\\0ratelance\\0" + // subdomain "ratelance" + // subdomain_sfx "" - ;; 我们希望解析结果指向 'ratelance.ton' 合约,而不是其所有者 - ;; 因此我们必须回答解析已完成 + "wallet"H 是 'ratelance.ton' 合约的地址 + // 我们希望解析结果指向 'ratelance.ton' 合约,而不是其所有者 + // 因此我们必须回答解析已完成 + "wallet"H 是 'ratelance.ton' 合约的地址 - ;; dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; - ;; _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; + // dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; + // _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; cell wallet_record = begin_cell().store_uint(0x9fd3, 16).store_builder(domain_nft_address).store_uint(0, 8).end_cell(); @@ -98,12 +98,12 @@ _bits(subdomain_sfx)); return (subdomain_bits, null()); } } else { - ;; subdomain "resolve-contract" - ;; subdomain_sfx "ton\\0ratelance\\0" - ;; 我们希望将 \\0 传递给下一个解析器,以便下一个解析器只处理一个字节 + // subdomain "resolve-contract" + // subdomain_sfx "ton\\0ratelance\\0" + // 我们希望将 \\0 传递给下一个解析器,以便下一个解析器只处理一个字节 - ;; 下一个解析器是 'resolve-contract<.ton>' 的合约 - ;; dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; + // 下一个解析器是 'resolve-contract<.ton>' 的合约 + // dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; cell resolver_record = begin_cell().store_uint(0xba93, 16).store_builder(domain_nft_address).end_cell(); return (subdomain_bits - slice_bits(subdomain_sfx) - 8, resolver_record); } @@ -167,12 +167,12 @@ _bits(subdomain_sfx)); global slice owner; global cell domains; -() load_data() impure { +() load_data() { slice ds = get_data().begin_parse(); owner = ds~load_msg_addr(); domains = ds~load_dict(); } -() save_data() impure { +() save_data() { set_data(begin_cell().store_slice(owner).store_dict(domains).end_cell()); } ``` @@ -181,16 +181,16 @@ global cell domains; ```func const int op::update_record = 0x537a3491; -;; op::update_record#537a3491 domain_name:^Cell record_key:uint256 -;; value:(Maybe ^Cell) = InMsgBody; +// op::update_record#537a3491 domain_name:^Cell record_key:uint256 +// value:(Maybe ^Cell) = InMsgBody; () recv_internal(cell in_msg, slice in_msg_body) { - if (in_msg_body.slice_empty?()) { return (); } ;; 简单的资金转移 + if (in_msg_body.slice_empty?()) { return (); } // 简单的资金转移 slice in_msg_full = in_msg.begin_parse(); if (in -_msg_full~load_uint(4) & 1) { return (); } ;; 弹回消息 +_msg_full~load_uint(4) & 1) { return (); } // 弹回消息 slice sender = in_msg_full~load_msg_addr(); load_data(); @@ -202,7 +202,7 @@ _msg_full~load_uint(4) & 1) { return (); } ;; 弹回消息 (cell records, _) = domains.udict_get_ref?(256, string_hash(domain)); int key = in_msg_body~load_uint(256); - throw_if(502, key == 0); ;; 不能更新“所有记录”的记录 + throw_if(502, key == 0); // 不能更新“所有记录”的记录 if (in_msg_body~load_uint(1) == 1) { cell value = in_msg_body~load_ref(); @@ -227,31 +227,31 @@ _msg_full~load_uint(4) & 1) { return (); } ;; 弹回消息 ```func (slice, slice) ~parse_sd(slice subdomain) { - ;; "test\0qwerty\0" -> "test" "qwerty\0" + // "test\0qwerty\0" -> "test" "qwerty\0" slice subdomain_sfx = subdomain; - while (subdomain_sfx~load_uint(8)) { } ;; 搜索零字节 + while (subdomain_sfx~load_uint(8)) { } // 搜索零字节 subdomain~skip_last_bits(slice_bits(subdomain_sfx)); return (subdomain, subdomain_sfx); } -(int, cell) dnsresolve(slice subdomain, int category) method_id { +get (int, cell) dnsresolve(slice subdomain, int category) { int subdomain_bits = slice_bits(subdomain); throw_unless(70, subdomain_bits % 8 == 0); if (subdomain.preload_uint(8) == 0) { subdomain~skip_bits(8); } - slice subdomain_suffix = subdomain~parse_sd(); ;; "test\0" -> "test" "" + slice subdomain_suffix = subdomain~parse_sd(); // "test\0" -> "test" "" int subdomain_suffix_bits = slice_bits(subdomain_suffix); load_data(); (cell records, _) = domains.udict_get_ref?(256, string_hash(subdomain)); - if (subdomain_suffix_bits > 0) { ;; 请求的内容超过 "\0" + if (subdomain_suffix_bits > 0) { // 请求的内容超过 "\0" category = "dns_next_resolver"H; } int resolved = subdomain_bits - subdomain_suffix_bits; - if (category == 0) { ;; 请求所有类别 + if (category == 0) { // 请求所有类别 return (resolved, records); } @@ -345,36 +345,36 @@ builder get_tme_nft_address_by_index(int index) inline { return (readable, target); } -slice decode_base64_address(slice readable) method_id { +get slice decode_base64_address(slice readable) { (slice _remaining, builder addr) = decode_base64_address_to(readable, begin_cell()); return addr.end_cell().begin_parse(); } -(int, cell) dnsresolve(slice subdomain, int category) method_id { +get (int, cell) dnsresolve(slice subdomain, int category) { int subdomain_bits = slice_bits(subdomain); throw_unless(70, (subdomain_bits % 8) == 0); - int starts_with_zero_byte = subdomain.preload_int(8) == 0; ;; 假设 'subdomain' 不为空 + int starts_with_zero_byte = subdomain.preload_int(8) == 0; // 假设 'subdomain' 不为空 if (starts_with_zero_byte) { subdomain~load_uint(8); - if (subdomain.slice_bits() == 0) { ;; 当前合约没有自己的 DNS 记录 + if (subdomain.slice_bits() == 0) { // 当前合约没有自己的 DNS 记录 return (8, null()); } } - ;; 我们正在加载某个子域名 - ;; 支持的子域名是 "ton\\0", "me\\0t\\0" 和 "address\\0" + // 我们正在加载某个子域名 + // 支持的子域名是 "ton\\0", "me\\0t\\0" 和 "address\\0" slice subdomain_sfx = null(); builder domain_nft_address = null(); if (subdomain.starts_with("746F6E00"s)) { - ;; 我们正在解析 - ;; "ton" \\0 \\0 [subdomain_sfx] + // 我们正在解析 + // "ton" \\0 \\0 [subdomain_sfx] subdomain~skip_bits(32); - ;; 读取域名 + // 读取域名 subdomain_sfx = subdomain; while (subdomain_sfx~load_uint(8)) { } @@ -382,10 +382,10 @@ slice decode_base64_address(slice readable) method_id { domain_nft_address = get_ton_dns_nft_address_by_index(slice_hash(subdomain)); } elseif (subdomain.starts_with("6D65007400"s)) { - ;; "t" \\0 "me" \\0 \\0 [subdomain_sfx] + // "t" \\0 "me" \\0 \\0 [subdomain_sfx] subdomain~skip_bits(40); - ;; 读取域名 + // 读取域名 subdomain_sfx = subdomain; while (subdomain_sfx~load_uint(8)) { } @@ -406,19 +406,19 @@ slice decode_base64_address(slice readable) method_id { } if (slice_empty?(subdomain_sfx)) { - ;; 解析域名的示例: - ;; [初始,此合约不可访问] "ton\\0resolve-contract\\0ton\\0ratelance\\0" - ;; [此合约可以访问] "ton\\0ratelance\\0" - ;; subdomain "ratelance" - ;; subdomain_sfx + // 解析域名的示例: + // [初始,此合约不可访问] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + // [此合约可以访问] "ton\\0ratelance\\0" + // subdomain "ratelance" + // subdomain_sfx "" - ;; 我们希望解析结果指向 'ratelance.ton' 合约,而不是其所有者 - ;; 因此我们必须回答解析已完成 + "wallet"H 是 'ratelance.ton' 合约的地址 + // 我们希望解析结果指向 'ratelance.ton' 合约,而不是其所有者 + // 因此我们必须回答解析已完成 + "wallet"H 是 'ratelance.ton' 合约的地址 - ;; dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; - ;; _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; + // dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; + // _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; cell wallet_record = begin_cell().store_uint(0x9fd3, 16).store_builder(domain_nft_address).store_uint(0, 8).end_cell(); @@ -432,15 +432,15 @@ slice decode_base64_address(slice readable) method_id { return (subdomain_bits, null()); } } else { - ;; 解析域名的示例: - ;; [初始,此合约不可访问] "ton\\0resolve-contract\\0ton\\0resolve-contract\\0ton\\0ratelance\\0" - ;; [此合约可以访问] "ton\\0resolve-contract\\0ton\\0ratelance\\0" - ;; subdomain "resolve-contract" - ;; subdomain_sfx "ton\\0ratelance\\0" - ;; 我们希望将 \\0 传递给下一个解析器,以便下一个解析器只处理一个字节 + // 解析域名的示例: + // [初始,此合约不可访问] "ton\\0resolve-contract\\0ton\\0resolve-contract\\0ton\\0ratelance\\0" + // [此合约可以访问] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + // subdomain "resolve-contract" + // subdomain_sfx "ton\\0ratelance\\0" + // 我们希望将 \\0 传递给下一个解析器,以便下一个解析器只处理一个字节 - ;; 下一个解析器是 'resolve-contract<.ton>' 的合约 - ;; dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; + // 下一个解析器是 'resolve-contract<.ton>' 的合约 + // dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; cell resolver_record = begin_cell().store_uint(0xba93, 16).store_builder(domain_nft_address).end_cell(); return (subdomain_bits - slice_bits(subdomain_sfx) - 8, resolver_record); } diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/network/adnl-tcp.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/network/adnl-tcp.md index f22ba2c6c9..728e405cda 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/network/adnl-tcp.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/network/adnl-tcp.md @@ -172,7 +172,7 @@ liteServer.runMethodResult mode:# id:tonNode.blockIdExt shardblk:tonNode.blockId FunC中的方法代码: ```func -(cell, cell) a2() method_id { +get (cell, cell) a2() { cell a = begin_cell().store_uint(0xAABBCC8, 32).end_cell(); cell b = begin_cell().store_uint(0xCCFFCC1, 32).end_cell(); return (a, b); diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/installation.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/installation.md index 7a503401e8..fb59ff86c3 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/installation.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/installation.md @@ -30,7 +30,7 @@ colorType="primary" sizeType={'sm'}> 从下表中下载二进制文件。请确保选择适合您操作系统的正确版本,并安装任何附加依赖项: -| 操作系统 | TON二进制文件 | fift | func | lite-client | 附加依赖项 | +| 操作系统 | TON二进制文件 | fift | FunC | lite-client | 附加依赖项 | |---------------|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|-------------|-------------------------------------------------------------------------------------| | MacOS x86-64 | [下载](https://github.com/ton-blockchain/ton/releases/latest/download/ton-mac-x86-64.zip) | [下载](https://github.com/ton-blockchain/ton/releases/latest/download/fift-mac-x86-64) | [下载](https://github.com/ton-blockchain/ton/releases/latest/download/func-mac-x86-64) | [下载](https://github.com/ton-blockchain/ton/releases/latest/download/lite-client-mac-x86-64) | | | MacOS arm64 | [下载](https://github.com/ton-blockchain/ton/releases/latest/download/ton-mac-arm64.zip) | ||| `brew install openssl ninja libmicrohttpd pkg-config` | diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/get-methods.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/get-methods.md index 8e8002b41d..5865b0215b 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/get-methods.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/get-methods.md @@ -19,7 +19,7 @@ Get方法是智能合约中用于查询特定数据的特殊函数。它们的 示例: ```func - int get_balance() method_id { + get int balance() { return get_data().begin_parse().preload_uint(64); } ``` @@ -29,7 +29,7 @@ Get方法是智能合约中用于查询特定数据的特殊函数。它们的 示例: ```func - (int, slice, slice, cell) get_wallet_data() method_id { + get (int, slice, slice, cell) wallet_data() { return load_data(); } ``` @@ -41,7 +41,7 @@ Get方法是智能合约中用于查询特定数据的特殊函数。它们的 示例: ```func - slice get_wallet_address(slice owner_address) method_id { + get slice wallet_address(slice owner_address) { (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); } @@ -52,7 +52,7 @@ Get方法是智能合约中用于查询特定数据的特殊函数。它们的 示例: ```func - (int) get_ready_to_be_used() method_id { + get (int) ready_to_be_used() { int ready? = now() >= 1686459600; return ready?; } @@ -65,7 +65,7 @@ Get方法是智能合约中用于查询特定数据的特殊函数。它们的 #### seqno() ```func -int seqno() method_id { +get int seqno() { return get_data().begin_parse().preload_uint(32); } ``` @@ -75,7 +75,7 @@ int seqno() method_id { #### get_subwallet_id() ```func -int get_subwallet_id() method_id { +get int get_subwallet_id() { return get_data().begin_parse().skip_bits(32).preload_uint(32); } ``` @@ -85,7 +85,7 @@ int get_subwallet_id() method_id { #### get_public_key() ```func -int get_public_key() method_id { +get int get_public_key() { var cs = get_data().begin_parse().skip_bits(64); return cs.preload_uint(256); } @@ -98,7 +98,7 @@ int get_public_key() method_id { #### get_wallet_data() ```func -(int, slice, slice, cell) get_wallet_data() method_id { +get (int, slice, slice, cell) get_wallet_data() { return load_data(); } ``` @@ -113,7 +113,7 @@ int get_public_key() method_id { #### get_jetton_data() ```func -(int, int, slice, cell, cell) get_jetton_data() method_id { +get (int, int, slice, cell, cell) get_jetton_data() { (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); return (total_supply, -1, admin_address, content, jetton_wallet_code); } @@ -124,7 +124,7 @@ int get_public_key() method_id { #### get_wallet_address(slice owner_address) ```func -slice get_wallet_address(slice owner_address) method_id { +get slice get_wallet_address(slice owner_address) { (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); } @@ -137,7 +137,7 @@ slice get_wallet_address(slice owner_address) method_id { #### get_nft_data() ```func -(int, int, slice, slice, cell) get_nft_data() method_id { +get (int, int, slice, slice, cell) get_nft_data() { (int init?, int index, slice collection_address, slice owner_address, cell content) = load_data(); return (init?, index, collection_address, owner_address, content); } @@ -148,7 +148,7 @@ slice get_wallet_address(slice owner_address) method_id { #### get_collection_data() ```func -(int, cell, slice) get_collection_data() method_id { +get (int, cell, slice) get_collection_data() { var (owner_address, next_item_index, content, _, _) = load_data(); slice cs = content.begin_parse(); return (next_item_index, cs~load_ref(), owner_address); @@ -160,7 +160,7 @@ slice get_wallet_address(slice owner_address) method_id { #### get_nft_address_by_index(int index) ```func -slice get_nft_address_by_index(int index) method_id { +get slice get_nft_address_by_index(int index) { var (_, _, _, nft_item_code, _) = load_data(); cell state_init = calculate_nft_item_state_init(index, nft_item_code); return calculate_nft_item_address(workchain(), state_init); @@ -172,7 +172,7 @@ slice get_nft_address_by_index(int index) method_id { #### royalty_params() ```func -(int, int, slice) royalty_params() method_id { +get (int, int, slice) royalty_params() { var (_, _, _, _, royalty) = load_data(); slice rs = royalty.begin_parse(); return (rs~load_uint(16), rs~load_uint(16), rs~load_msg_addr()); @@ -184,13 +184,13 @@ slice get_nft_address_by_index(int index) method_id { #### get_nft_content(int index, cell individual_nft_content) ```func -cell get_nft_content(int index, cell individual_nft_content) method_id { +get cell get_nft_content(int index, cell individual_nft_content) { var (_, _, content, _, _) = load_data(); slice cs = content.begin_parse(); cs~load_ref(); slice common_content = cs~load_ref().begin_parse(); return (begin_cell() - .store_uint(1, 8) ;; 离线标签 + .store_uint(1, 8) // 离线标签 .store_slice(common_content) .store_ref(individual_nft_content) .end_cell()); @@ -225,8 +225,8 @@ cell get_nft_content(int index, cell individual_nft_content) method_id { 假设有一个合约,其中有以下get方法: ```func -(int) get_total() method_id { - return get_data().begin_parse().preload_uint(32); ;; load and return the 32-bit number from the data +get (int) get_total() { + return get_data().begin_parse().preload_uint(32); // load and return the 32-bit number from the data } ``` @@ -313,11 +313,11 @@ it('应该从get方法返回正确的数字', async () => { ```func #include "imports/stdlib.fc"; -int get_total() method_id { +get int get_total() { return get_data().begin_parse().preload_uint(32); } -() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure { +() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) { if (in_msg_body.slice_bits() < 32) { return (); } @@ -326,23 +326,23 @@ int get_total() method_id { cs~skip_bits(4); slice sender = cs~load_msg_addr(); - int op = in_msg_body~load_uint(32); ;; load the operation code + int op = in_msg_body~load_uint(32); // load the operation code - if (op == 1) { ;; increase and update the number + if (op == 1) { // increase and update the number int number = in_msg_body~load_uint(32); int total = get_total(); total += number; set_data(begin_cell().store_uint(total, 32).end_cell()); } - elseif (op == 2) { ;; query the number + elseif (op == 2) { // query the number int total = get_total(); send_raw_message(begin_cell() .store_uint(0x18, 6) .store_slice(sender) .store_coins(0) - .store_uint(0, 107) ;; default message headers (see sending messages page) - .store_uint(3, 32) ;; response operation code - .store_uint(total, 32) ;; the requested number + .store_uint(0, 107) // default message headers (see sending messages page) + .store_uint(3, 32) // response operation code + .store_uint(total, 32) // the requested number .end_cell(), 64); } } diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/random-number-generation.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/random-number-generation.md index 6d518ae660..62b545d637 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/random-number-generation.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/random-number-generation.md @@ -20,7 +20,7 @@ ```func randomize_lt(); -int x = random(); ;; 用户无法预测这个数字 +int x = random(); // 用户无法预测这个数字 ``` 然而,你应该注意验证者或协作者仍然可能影响随机数的结果,因为他们决定了当前区块的seed。 @@ -40,55 +40,55 @@ int x = random(); ;; 用户无法预测这个数字 让我们以一个简单的彩票合约为例。用户将向它发送1 TON,有50%的机会会得到2 TON回报。 ```func -;; 设置回声合约地址 +// 设置回声合约地址 const echo_address = "Ef8Nb7157K5bVxNKAvIWreRcF0RcUlzcCA7lwmewWVNtqM3s"a; -() recv_internal (int msg_value, cell in_msg_full, slice in_msg_body) impure { +() recv_internal (int msg_value, cell in_msg_full, slice in_msg_body) { var cs = in_msg_full.begin_parse(); var flags = cs~load_uint(4); - if (flags & 1) { ;; 忽略弹回的消息 + if (flags & 1) { // 忽略弹回的消息 return (); } slice sender = cs~load_msg_addr(); int op = in_msg_body~load_uint(32); - if ((op == 0) & equal_slice_bits(in_msg_body, "bet")) { ;; 用户下注 - throw_unless(501, msg_value == 1000000000); ;; 1 TON + if ((op == 0) & equal_slice_bits(in_msg_body, "bet")) { // 用户下注 + throw_unless(501, msg_value == 1000000000); // 1 TON send_raw_message( begin_cell() .store_uint(0x18, 6) .store_slice(echo_address) .store_coins(0) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息头部(见发送消息页面) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息头部(见发送消息页面) - .store_uint(1, 32) ;; 让1成为我们合约中的回声操作码 - .store_slice(sender) ;; 转发用户地址 + .store_uint(1, 32) // 让1成为我们合约中的回声操作码 + .store_slice(sender) // 转发用户地址 .end_cell(), - 64 ;; 发送剩余的消息值 + 64 // 发送剩余的消息值 ); } - elseif (op == 1) { ;; 回声 - throw_unless(502, equal_slice_bits(sender, echo_address)); ;; 只接受我们回声合约的回声 + elseif (op == 1) { // 回声 + throw_unless(502, equal_slice_bits(sender, echo_address)); // 只接受我们回声合约的回声 slice user = in_msg_body~load_msg_addr(); - {- + /* 此时我们已经跳过了1+个区块 因此让我们生成随机数 - -} + */ randomize_lt(); - int x = rand(2); ;; 生成一个随机数(0或1) - if (x == 1) { ;; 用户赢了 + int x = rand(2); // 生成一个随机数(0或1) + if (x == 1) { // 用户赢了 send_raw_message( begin_cell() .store_uint(0x18, 6) .store_slice(user) - .store_coins(2000000000) ;; 2 TON - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认消息头部(见发送消息页面) + .store_coins(2000000000) // 2 TON + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认消息头部(见发送消息页面) .end_cell(), - 3 ;; 忽略错误并单独支付费用 + 3 // 忽略错误并单独支付费用 ); } } diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/messages.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/messages.md index 8532f34f58..6ba72154a2 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/messages.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/messages.md @@ -63,29 +63,29 @@ ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt 消息的直接序列化如下所示: ```func var msg = begin_cell() - .store_uint(0, 1) ;; tag - .store_uint(1, 1) ;; ihr_disabled - .store_uint(1, 1) ;; allow bounces - .store_uint(0, 1) ;; not bounced itself + .store_uint(0, 1) // tag + .store_uint(1, 1) // ihr_disabled + .store_uint(1, 1) // allow bounces + .store_uint(0, 1) // not bounced itself .store_slice(source) .store_slice(destination) - ;; serialize CurrencyCollection (see below) + // serialize CurrencyCollection (see below) .store_coins(amount) .store_dict(extra_currencies) - .store_coins(0) ;; ihr_fee - .store_coins(fwd_value) ;; fwd_fee - .store_uint(cur_lt(), 64) ;; lt of transaction - .store_uint(now(), 32) ;; unixtime of transaction - .store_uint(0, 1) ;; no init-field flag (Maybe) - .store_uint(0, 1) ;; inplace message body flag (Either) + .store_coins(0) // ihr_fee + .store_coins(fwd_value) // fwd_fee + .store_uint(cur_lt(), 64) // lt of transaction + .store_uint(now(), 32) // unixtime of transaction + .store_uint(0, 1) // no init-field flag (Maybe) + .store_uint(0, 1) // inplace message body flag (Either) .store_slice(msg_body) .end_cell(); ``` 然而,开发者通常使用快捷方式而不是逐步序列化所有字段。因此,让我们考虑如何使用[elector-code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/elector-code.fc#L153)中的示例从智能合约发送消息。 ```func -() send_message_back(addr, ans_tag, query_id, body, grams, mode) impure inline_ref { - ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000 +() send_message_back(addr, ans_tag, query_id, body, grams, mode) inline_ref { + // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000 var msg = begin_cell() .store_uint(0x18, 6) .store_slice(addr) diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/ton-hack-challenge-1.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/ton-hack-challenge-1.md index 26d3802a88..eb283e0d65 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/ton-hack-challenge-1.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/ton-hack-challenge-1.md @@ -41,15 +41,15 @@ TON Hack挑战赛于10月23日举行。在TON主网上部署了几个带有人 投票权在消息中以整数形式存储。所以攻击者可以在转移投票权时发送一个负值,并获得无限投票权。 ```func -(cell,()) transfer_voting_power (cell votes, slice from, slice to, int amount) impure { +(cell,()) transfer_voting_power (cell votes, slice from, slice to, int amount) { int from_votes = get_voting_power(votes, from); int to_votes = get_voting_power(votes, to); from_votes -= amount; to_votes += amount; - ;; No need to check that result from_votes is positive: set_voting_power will throw for negative votes - ;; throw_unless(998, from_votes > 0); + // No need to check that result from_votes is positive: set_voting_power will throw for negative votes + // throw_unless(998, from_votes > 0); votes~set_voting_power(from, from_votes); votes~set_voting_power(to, to_votes); @@ -75,7 +75,7 @@ if(in_msg_body.slice_bits() > 0) { set_seed(seed); var balance = get_balance().pair_first(); if(balance > 5000 * 1000000000) { - ;; 禁止过大的奖池 + // 禁止过大的奖池 raw_reserve( balance - 5000 * 1000000000, 0); } if(rand(10000) == 7777) { ...send reward... } @@ -102,11 +102,11 @@ if(rand(10000) == 7777) { ...send reward... } ```func int mode = null(); if (op == op_not_winner) { - mode = 64; ;; 退还剩余的支票TON - ;; addr_hash 对应于支票请求者 + mode = 64; // 退还剩余的支票TON + // addr_hash 对应于支票请求者 } else { - mode = 128; ;; 颁发奖金 - ;; addr_hash 对应于中奖条目中的取款地址 + mode = 128; // 颁发奖金 + // addr_hash 对应于中奖条目中的取款地址 } ``` @@ -134,9 +134,9 @@ slice safe_execute(int image, (int -> slice) dehasher) inline { slice preimage = try_execute(image, dehasher); - ;; 如果dehasher破坏了它,恢复c4 + // 如果dehasher破坏了它,恢复c4 set_data(c4); - ;; 如果dehasher破坏了它们,清除操作 + // 如果dehasher破坏了它们,清除操作 set_c5(begin_cell().end_cell()); return preimage; diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/toncli.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/toncli.md index 441d30e0db..06a4655f7d 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/toncli.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/toncli.md @@ -70,7 +70,7 @@ int __test_example() { 由于我们将进行大量测试,需要频繁更新`c4`寄存器,因此我们将创建一个辅助函数,它将将`c4`写为零 ```js -() set_default_initial_data() impure { +() set_default_initial_data() { set_data(begin_cell().store_uint(0, 64).end_cell()); } ``` @@ -80,8 +80,6 @@ int __test_example() { * `end_cell()`- 创建cell * `set_data()` - 将cell写入寄存器c4 -`impure`是一个关键词,表示该函数更改了智能合约数据。 - 我们得 到一个将在测试函数主体中使用的函数 diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig.md index b97f61ec2f..163a44f56d 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig.md @@ -28,7 +28,7 @@ description: 本教程结束时,您将在TON区块链上部署了多签合约 - 从[安装](/develop/smart-contracts/environment/installation)部分中安装`func`、`fift`、`lite-client`二进制文件和`fiftlib`。 - 克隆[库](https://github.com/akifoq/multisig)并在CLI中打开其目录。 -```cpp +``` https://github.com/akifoq/multisig.git cd ~/multisig ``` @@ -45,7 +45,7 @@ cd ~/multisig 使用以下命令将合约编译为Fift: -```cpp +```bash func -o multisig-code.fif -SPA stdlib.fc multisig-code.fc ``` @@ -54,7 +54,7 @@ func -o multisig-code.fif -SPA stdlib.fc multisig-code.fc 要创建一个密钥,您需要运行: -```cpp +```bash fift -s new-key.fif $KEY_NAME$ ``` @@ -62,7 +62,7 @@ fift -s new-key.fif $KEY_NAME$ 例如: -```cpp +```bash fift -s new-key.fif multisig_key ``` @@ -96,7 +96,7 @@ PubH821csswh8R1uO9rLYyP1laCpYWxhNkx+epOkqwdWXgzY4 之后,您需要运行: -```cpp +```bash fift -s new-multisig.fif 0 $WALLET_ID$ wallet $KEYS_COUNT$ ./keys.txt ``` @@ -166,7 +166,7 @@ sendfile ./wallet-create.boc 首先,您需要创建一个消息请求: -```cpp +```bash fift -s create-msg.fif $ADDRESS$ $AMOUNT$ $MESSAGE$ ``` @@ -176,7 +176,7 @@ fift -s create-msg.fif $ADDRESS$ $AMOUNT$ $MESSAGE$ 例如: -```cpp +```bash fift -s create-msg.fif EQApAj3rEnJJSxEjEHVKrH3QZgto_MQMOmk8l72azaXlY1zB 0.1 message ``` diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/wallet.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/wallet.md index fc4bef7bd1..b6833fd5eb 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/wallet.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/wallet.md @@ -165,22 +165,22 @@ func main() { 通常,在 TON 区块链上有两种交易类型:`internal` 和 `external`。外部交易允许从外部世界向区块链发送消息,从而与接受此类交易的智能合约进行通信。负责执行此过程的函数如下: ```func -() recv_external(slice in_msg) impure { - ;; 一些代码 +() recv_external(slice in_msg) { + // 一些代码 } ``` 在我们深入研究钱包之前,让我们先看看钱包如何接受外部交易。在 TON 上,所有钱包都持有所有者的 `公钥`、`seqno` 和 `subwallet_id`。接收到外部交易时,钱包使用 `get_data()` 方法从钱包的存储部分中检索数据。然后进行多个验证流程,并决定是否接受此交易。这个过程的完成如下: ```func -() recv_external(slice in_msg) impure { - var signature = in_msg~load_bits(512); ;; 从消息体中获取签名 +() recv_external(slice in_msg) { + var signature = in_msg~load_bits(512); // 从消息体中获取签名 var cs = in_msg; - var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); ;; 从消息体中获取其他值 - throw_if(35, valid_until <= now()); ;; 检查交易的有效性 - var ds = get_data().begin_parse(); ;; 从存储获取数据并将其转换为可读取值的切片 - var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); ;; 从存储中读取值 - ds.end_parse(); ;; 确保变量 ds 中没有任何数据 + var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); // 从消息体中获取其他值 + throw_if(35, valid_until <= now()); // 检查交易的有效性 + var ds = get_data().begin_parse(); // 从存储获取数据并将其转换为可读取值的切片 + var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); // 从存储中读取值 + ds.end_parse(); // 确保变量 ds 中没有任何数据 throw_unless(33, msg_seqno == stored_seqno); throw_unless(34, subwallet_id == stored_subwallet); throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); @@ -268,8 +268,8 @@ throw_if(35, valid_until <= now()); ```func cs~touch(); while (cs.slice_refs()) { - var mode = cs~load_uint(8); ;; 加载交易模式 - send_raw_message(cs~load_ref(), mode); ;; 使用 load_ref() 将每一个新的内部消息作为一个带有 load_ref() 的cell,并发送它 + var mode = cs~load_uint(8); // 加载交易模式 + send_raw_message(cs~load_ref(), mode); // 使用 load_ref() 将每一个新的内部消息作为一个带有 load_ref() 的cell,并发送它 } ``` @@ -333,26 +333,26 @@ while (cs.slice_refs()) { ```func var msg = begin_cell() - .store_uint(0x18, 6) ;; 或者 0x10 代表不可弹回 + .store_uint(0x18, 6) // 或者 0x10 代表不可弹回 .store_slice(to_address) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认的消息头(请参阅发送消息页面) - ;; 作为存储体 + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认的消息头(请参阅发送消息页面) + // 作为存储体 ``` 让我们首先考虑 `0x18` 和 `0x10`(x - 16 进制),这些十六进制数是按以下方式排列的(考虑到我们分配了 6 个位):`011000` 和 `010000`。这意味着,可以将上述代码重写为以下内容: ```func var msg = begin_cell() - .store_uint(0, 1) ;; 这个位表示我们发送了一个内部消息,与 int_msg_info$0 对应 - .store_uint(1, 1) ;; IHR 禁用 - .store_uint(1, 1) ;; 或者 .store_uint(0, 1) 对于 0x10 | 退回 - .store_uint(0, 1) ;; 退回 - .store_uint(0, 2) ;; src -> 两个零位代表 addr_none + .store_uint(0, 1) // 这个位表示我们发送了一个内部消息,与 int_msg_info$0 对应 + .store_uint(1, 1) // IHR 禁用 + .store_uint(1, 1) // 或者 .store_uint(0, 1) 对于 0x10 | 退回 + .store_uint(0, 1) // 退回 + .store_uint(0, 2) // src -> 两个零位代表 addr_none .store_slice(to_address) .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; 默认的消息头(请参阅发送消息页面) - ;; 作为存储体 + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) // 默认的消息头(请参阅发送消息页面) + // 作为存储体 ``` 现在我们来详细解释每个选项: @@ -376,14 +376,14 @@ Src | Src 是发送者地址。在这种情况下,写入了两个零位以指 ```func ... - .store_uint(0, 1) ;; Extra currency - .store_uint(0, 4) ;; IHR fee - .store_uint(0, 4) ;; Forwarding fee - .store_uint(0, 64) ;; Logical time of creation - .store_uint(0, 32) ;; UNIX time of creation - .store_uint(0, 1) ;; State Init - .store_uint(0, 1) ;; Message body - ;; 作为存储体 + .store_uint(0, 1) // Extra currency + .store_uint(0, 4) // IHR fee + .store_uint(0, 4) // Forwarding fee + .store_uint(0, 64) // Logical time of creation + .store_uint(0, 32) // UNIX time of creation + .store_uint(0, 1) // State Init + .store_uint(0, 1) // Message body + // 作为存储体 ``` 选项 | 说明 :---: | :---: @@ -847,7 +847,7 @@ npm i --save @ton-community/func-js ``` :::info -如果您的IDE插件与`stdlib.fc`文件中的`() set_seed(int) impure asm "SETRAND";`冲突,这没关系。 +如果您的IDE插件与`stdlib.fc`文件中的`() set_seed(int) asm "SETRAND";`冲突,这没关系。 ::: 请记住,在`wallet_v3.fc`文件的开头添加以下行,以指示将在下面使用stdlib中的函数: @@ -2096,21 +2096,21 @@ if err != nil { 首先,让我们查看[高负载钱包智能合约的代码结构](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/highload-wallet-v2-code.fc): ```func -() recv_external(slice in_msg) impure { - var signature = in_msg~load_bits(512); ;; 从消息体中获取签名 +() recv_external(slice in_msg) { + var signature = in_msg~load_bits(512); // 从消息体中获取签名 var cs = in_msg; - var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); ;; 从消息体中获取其余值 - var bound = (now() << 32); ;; 位左移操作 - throw_if(35, query_id < bound); ;; 如果交易已过期则抛出错误 + var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); // 从消息体中获取其余值 + var bound = (now() << 32); // 位左移操作 + throw_if(35, query_id < bound); // 如果交易已过期则抛出错误 var ds = get_data().begin_parse(); - var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); ;; 从存储中读取值 - ds.end_parse(); ;; 确保 ds 中没有任何东西 - (_, var found?) = old_queries.udict_get?(64, query_id); ;; 检查是否已经存在此类请求 - throw_if(32, found?); ;; 如果是则抛出错误 + var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); // 从存储中读取值 + ds.end_parse(); // 确保 ds 中没有任何东西 + (_, var found?) = old_queries.udict_get?(64, query_id); // 检查是否已经存在此类请求 + throw_if(32, found?); // 如果是则抛出错误 throw_unless(34, subwallet_id == stored_subwallet); throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); - var dict = cs~load_dict(); ;; 获取包含消息的字典 - cs.end_parse(); ;; 确保 cs 中没有任何东西 + var dict = cs~load_dict(); // 获取包含消息的字典 + cs.end_parse(); // 确保 cs 中没有任何东西 accept_message(); ``` @@ -2132,10 +2132,10 @@ if err != nil { 如果相同的交易请求已经存在,合约将不会接受它,因为它已经被处理过了: ```func -var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); ;; 从存储中读取值 -ds.end_parse(); ;; 确保 ds 中没有任何东西 -(_, var found?) = old_queries.udict_get?(64, query_id); ;; 检查是否已经存在此类请求 -throw_if(32, found?); ;; 如果是则抛出错误 +var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); // 从存储中读取值 +ds.end_parse(); // 确保 ds 中没有任何东西 +(_, var found?) = old_queries.udict_get?(64, query_id); // 检查是否已经存在此类请求 +throw_if(32, found?); // 如果是则抛出错误 ``` 通过这种方式,我们**被保护免受重复交易的影响**,这是普通钱包中 seqno 的作用。 @@ -2145,14 +2145,14 @@ throw_if(32, found?); ;; 如果是则抛出错误 合约接受外部消息后,将开始循环,在循环中取出存储在字典中的 `slices`。这些切片存储了交易模式和交易本身。发送新交易一直进行,直到字典为空。 ```func -int i = -1; ;; 我们写 -1 是因为它将是所有字典键中的最小值 +int i = -1; // 我们写 -1 是因为它将是所有字典键中的最小值 do { - (i, var cs, var f) = dict.idict_get_next?(16, i); ;; 获取键及其对应的最小键值,这个键值大于 i - if (f) { ;; 检查是否找到了任何值 - var mode = cs~load_uint(8); ;; 加载交易模式 - send_raw_message(cs~load_ref(), mode); ;; 加载交易本身并发送 + (i, var cs, var f) = dict.idict_get_next?(16, i); // 获取键及其对应的最小键值,这个键值大于 i + if (f) { // 检查是否找到了任何值 + var mode = cs~load_uint(8); // 加载交易模式 + send_raw_message(cs~load_ref(), mode); // 加载交易本身并发送 } -} until (~ f); ;; 如果找到任何值则继续 +} until (~ f); // 如果找到任何值则继续 ``` > 💡 有用的链接: @@ -2166,18 +2166,18 @@ do { 通常情况下,[TON上的智能合约需要为自己的存储付费](/develop/smart-contracts/fees#storage-fee)。这意味着智能合约可以存储的数据量是有限的,以防止高网络交易费用。为了让系统更高效,超过 64 秒的交易将从存储中移除。按照以下方式进行: ```func -bound -= (64 << 32); ;; 清除记录,这些记录超过 64 秒前已过期 -old_queries~udict_set_builder(64, query_id, begin_cell()); ;; 将当前查询添加到字典中 -var queries = old_queries; ;; 将字典复制到另一个变量中 +bound -= (64 << 32); // 清除记录,这些记录超过 64 秒前已过期 +old_queries~udict_set_builder(64, query_id, begin_cell()); // 将当前查询添加到字典中 +var queries = old_queries; // 将字典复制到另一个变量中 do { var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64); f~touch(); - if (f) { ;; 检查是否找到了任何值 - f = (i < bound); ;; 检查是否超过 64 秒后过期 + if (f) { // 检查是否找到了任何值 + f = (i < bound); // 检查是否超过 64 秒后过期 } if (f) { - old_queries = old_queries'; ;; 如果是,则在我们的字典中保存更改 - last_cleaned = i; ;; 保存最后移除的查询 + old_queries = old_queries'; // 如果是,则在我们的字典中保存更改 + last_cleaned = i; // 保存最后移除的查询 } } until (~ f); ``` @@ -2193,7 +2193,7 @@ do { 如果您之前没有使用过位运算,那么这个部分可能会显得有些复杂。在智能合约代码中可以看到以下代码行: ```func -var bound = (now() << 32); ;; 位左移操作 +var bound = (now() << 32); // 位左移操作 ``` 结果,在右侧的数字上添加了 32 位。这意味着 **现有值向左移动 32 位**。举例来说,让我们考虑数字 3 并将其翻译成二进制形式,结果是 11。应用 `3 << 2` 操作,11 移动了 2 位。这意味着在字符串的右侧添加了两位。最后,我们得到了 1100,即 12。 @@ -2202,14 +2202,14 @@ var bound = (now() << 32); ;; 位左移操作 接下来,让我们考虑以下代码行: ```func -bound -= (64 << 32); ;; 清除超过 64 秒之前过期的记录 +bound -= (64 << 32); // 清除超过 64 秒之前过期的记录 ``` 在上面,我们执行了一个操作,将数字 64 向左移动 32 位,以**减去 64 秒**的时间戳。这样我们就可以比较过去的 query_ids,看看它们是否小于接收到的值。如果是这样,它们就超过了 64 秒: ```func -if (f) { ;; 检查是否找到了任何值 - f = (i < bound); ;; 检查是否超过 64 秒后过期 +if (f) { // 检查是否找到了任何值 + f = (i < bound); // 检查是否超过 64 秒后过期 } ``` 为了更好地理解,让我们使用 `1625918400` 作为时间戳的示例。它的二进制表示(左侧添加零以得到 32 位)是 01100000111010011000101111000000。执行 32 位位左移操作后,我们数字的二进制表示末尾会出现 32 个零。 @@ -2242,7 +2242,7 @@ int get_public_key() | 检索公钥。我们之前已经讨论过这个方法。 让我们仔细看看 `int processed?(int query_id)` 方法,以帮助我们了解为什么我们需要使用 last_cleaned: ```func -int processed?(int query_id) method_id { +get int processed?(int query_id) { var ds = get_data().begin_parse(); var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); ds.end_parse(); diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-exit-codes.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-exit-codes.md index 05f97543bb..b9ed82517c 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-exit-codes.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-exit-codes.md @@ -36,7 +36,7 @@ | `40` | Action Phase | 处理消息的资金不足。当只有足够的gas覆盖消息的一部分时,但不完全覆盖时,会抛出此错误。 | | `43` | Action Phase | 超出库中的最大cell数或 Merkle 树的最大深度。 | -1 如果在 func 合约中遇到此类异常,这可能意味着汇编声明中存在类型错误。 +1 如果在 FunC 合约中遇到此类异常,这可能意味着汇编声明中存在类型错误。 :::info 通常,您会看到Exit code `0xffff`(十进制形式为 65535)。这通常意味着合约不认识接收到的操作码。在编写合约时,开发人员自己设置的此代码。 diff --git a/i18n/mandarin/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-upgrade-2023-07.md b/i18n/mandarin/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-upgrade-2023-07.md index 204aa99285..97ba43a385 100644 --- a/i18n/mandarin/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-upgrade-2023-07.md +++ b/i18n/mandarin/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-upgrade-2023-07.md @@ -19,7 +19,7 @@ 使用 **10** 作为代码对于 tvm 的 Everscale 更新兼容。 **11** 当前,传入消息的值在 TVM 初始化后以堆栈形式呈现,因此如果在执行过程中需要, -则需要将其存储到全局变量或通过本地变量传递(在 funC 级别看起来像所有函数中的额外 `msg_value` 参数)。通过将其放在 **11** 元素中,我们将重复合约余额的行为:它既出现在堆栈中,也出现在 c7 中。 +则需要将其存储到全局变量或通过本地变量传递(在 FunC 级别看起来像所有函数中的额外 `msg_value` 参数)。通过将其放在 **11** 元素中,我们将重复合约余额的行为:它既出现在堆栈中,也出现在 c7 中。 **12** 目前计算存储费用的唯一方法是在先前的交易中存储余额,以某种方式计算 prev 交易中的 gas 用量,然后与当前余额减去消息值进行比较。与此同时,经常希望考虑存储费用。 diff --git a/sidebars.js b/sidebars.js index c8b49b41f5..63dec0a90a 100644 --- a/sidebars.js +++ b/sidebars.js @@ -502,7 +502,6 @@ const sidebars = { 'develop/func/global_variables', 'develop/func/compiler_directives', 'develop/func/statements', - 'develop/func/builtins', 'develop/func/dictionaries', 'develop/func/stdlib', ], diff --git a/src/theme/prism/prism-func.js b/src/theme/prism/prism-func.js index aaa892a9be..09bc472de0 100644 --- a/src/theme/prism/prism-func.js +++ b/src/theme/prism/prism-func.js @@ -28,18 +28,18 @@ 'comment': [ { - pattern: /;;.*/, + pattern: /\/\/.*/, lookbehind: true, greedy: true }, { - pattern: /\{-[\s\S]*?(?:-\}|$)/, + pattern: /\/\*[\s\S]*?\*\//, lookbehind: true, greedy: true }, ], - 'keyword': /\b(?:_(?=\s*:)|asm|const|do|else|elseif|elseifnot|forall|global|if|ifnot|impure|inline|inline_ref|method_id|repeat|return|until|while)\b/, + 'keyword': /\b(?:_(?=\s*:)|asm|const|do|else|elseif|elseifnot|forall|global|if|ifnot|impure|pure|builtin|get|inline|inline_ref|method_id|repeat|return|until|while|try|catch)\b/, 'boolean': /\b(?:false|true)\b/, 'builtin': /\b(?:_|builder|cell|cont|int|slice|tuple|var)\b/, @@ -50,4 +50,4 @@ 'operator': /(<=>|>=|<=|!=|==|~>>=|~>>|\/%|\^%=|\^%|~%|\^\/=|\^\/|~\/=|~\/|\+=|-=|\*=|\/=|%=|<<=|>>=|\^>>=|\^>>|&=|>>|<<|\^=|\|=|\^|=|~|\/|%|-|\*|\+|>|<|&|\||:|\?)/, 'punctuation': /[\.;\(\),\[\]~\{\}]/, }; -}(Prism)); \ No newline at end of file +}(Prism));