From 05514b899fb51b6e8467a0f0df8c72b840c34574 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sun, 10 Mar 2024 17:50:00 +0100 Subject: [PATCH] feat[66]: Cookbook is now part of the Book MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a bonus — all links on it are made relative and updated in accordance to the new structure. --- pages/book/_meta.js | 1 + pages/book/cookbook.mdx | 503 +++++++++++++++++++++++++++++++++++++++ pages/book/index.mdx | 14 +- pages/language/index.mdx | 4 +- 4 files changed, 519 insertions(+), 3 deletions(-) create mode 100644 pages/book/cookbook.mdx diff --git a/pages/book/_meta.js b/pages/book/_meta.js index a367da58..3a2c4388 100644 --- a/pages/book/_meta.js +++ b/pages/book/_meta.js @@ -2,6 +2,7 @@ export default { index: 'Overview', guides: 'Guides', cs: 'Cheatsheets', + cookbook: 'Cookbook', '--': { type: 'separator', }, diff --git a/pages/book/cookbook.mdx b/pages/book/cookbook.mdx new file mode 100644 index 00000000..585c98cd --- /dev/null +++ b/pages/book/cookbook.mdx @@ -0,0 +1,503 @@ +import { Callout } from 'nextra/components' + +# Cookbook + +The core reason for creating the Tact Cookbook is to collect all the experience from Tact developers in one place so that future developers will use it. Compared to the rest of the documentation, this page is more focused on everyday tasks every Tact developer resolves during the development of smart contracts. + +## Basics + +### How to write a Hello World smart contract + +```tact +contract HelloWorld { + // Note, that an empty init function won't be needed starting with Tact 1.3.0, + // see: https://github.com/tact-lang/tact/pull/167 + init() {} + get fun greeting(): String { + return "Hello, World!"; + } +} +``` + +### How to write an 'if' statement in Tact + +Tact supports `if` statements in a similar syntax to most programming languages. The condition of the statement can be any boolean expression. + +```tact +let value: Int = 9001; + +if (value > 10) { + // do something +} + +if (value > 100) { + // do something +} else { + // do something else +} + +if (value > 9000) { + // do something +} else if (value > 500) { + // do another thing +} else { + // do something else +} +``` + +## Loops + +### How to write a repeat loop + +Please make sure the input number for the [repeat](/book/statements#repeat-loop) loop statement is within the range of an int32 data type, as an exception will be thrown otherwise. + +```tact +let sum: Int = 0; +let i: Int = 0; + +repeat (10) { // repeat exactly 10 times + i = i + 1; + sum = sum + i; +} + +``` + + + **Useful links:**\ + [`repeat` loop in the Book](/book/statements#repeat-loop)\ + [Loops in Tact-By-Example](https://tact-by-example.org/04-loops) + + + +### How to write a while loop + +```tact +let i: Int = 0; + +while (i < 10) { + i = i + 1; +} +``` + + + + **Useful links:**\ + [`while` loop in the Book](/book/statements#while-loop)\ + [Loops in Tact-By-Example](https://tact-by-example.org/04-loops) + + + + +### How to write a do until loop + +When we need the cycle to run at least once, we use the [do-until](/book/statements#until-loop) loop. + +```tact +let num: Int; // A variable to store the random number + +// A do until loop that repeats until num is equal to 5 +do { + num = random(0, 9); // get a random number between 0 and 9 +} until (num == 5); // stop loop if num is equal to 5 + +dump("The loop is over!"); +``` + + + + **Useful links:**\ + [`do-until` loop in the Book](/book/statements#until-loop)\ + [`random()` in Language→Reference](/language/ref/random#random)\ + [Loops in Tact-By-Example](https://tact-by-example.org/04-loops) + + + +## Map + +```tact +// Create empty map +let m: map = emptyMap(); + +// Add key/value to the map +m.set(1, "a"); + +// Get value by key from the map +let first: String? = m.get(1); + +// Check key is exists +if (first == null) { + // do something... +} else { + // Cast value if key exists + let firstStr: String = first!!; + // do something... +} +``` + + + + **Useful links:**\ + [Map type in the Book](/book/types#maps) + + + +## Slice + +### How to determine if slice is empty + +A Slice is considered *empty* if it has no stored `data` **and** no stored `references`. + +Use `empty()` method to check if a Slice is empty. + +```tact +// Create an empty slice with no data and no refs +let empty_slice: Slice = emptyCell().asSlice(); +// Returns `true`, because `empty_slice` doesn't have any data or refs +empty_slice.empty(); + +// Create a slice with some data +let slice_with_data: Slice = beginCell(). + storeUint(42, 8). + asSlice(); +// Returns `false`, because the slice has some data +slice_with_data.empty(); + +// Create a slice with a reference to an empty cell +let slice_with_refs: Slice = beginCell(). + storeRef(emptyCell()). + asSlice(); +// Returns `false`, because the slice has a reference +slice_with_refs.empty(); + +// Create a slice with data and a reference +let slice_with_data_and_refs: Slice = beginCell(). + storeUint(42, 8). + storeRef(emptyCell()). + asSlice(); +// Returns `false`, because the slice has both data and reference +slice_with_data_and_refs.empty(); +``` + +### How to determine if a slice has no refs (but may have bits) + +```tact +let slice_with_data: Slice = beginCell(). + storeUint(0, 1). + asSlice(); // create a slice with data but without refs +let refsCount: Int = slice_with_data.refs(); // 0 +let hasNoRefs: Bool = slice_with_data.refsEmpty(); // true +``` + +### How to determine if a Slice has no data (no bits, but may have refs) + +```tact +let slice_with_data: Slice = beginCell(). + storeRef(emptyCell()). + asSlice(); // create a slice with ref but without data +let bitsCount: Int = slice_with_data.bits(); // 0 +let hasNoData: Bool = slice_with_data.dataEmpty(); // true +``` + +### How to determine if Slices are equal + +```tact +fun areSlicesEqual(a: Slice, b: Slice): Bool { + return a.hash() == b.hash(); +} + +let firstSlice: Slice = "A".asSlice(); +let secondSlice: Slice = "A".asSlice(); + +let result: Bool = areSlicesEqual(firstSlice, secondSlice); +dump(result) // true; +``` + + + + **Useful links:**\ + [`String.asSlice()` in Language→Reference](/language/ref/strings#stringasslice) + + + +## Cell + +### How to determine if a Cell is empty + +To check if there is any data in the Cell, we should first convert it to the Slice. If we are only interested in having bits, we should use `dataEmpty()`, if only refs - `refsEmpty()`. In case we want to check for the presence of any data, regardless of whether it is a bit or ref, we need to use `empty()`. + +```tact +// Create an empty cell with no data and no refs +let empty_cell: Cell = emptyCell(); // alias for beginCell().endCell() +// Present `cell` as a `slice` to parse it. +let slice: Slice = empty_cell.asSlice(); +// Returns `true`, because `slice` doesn't have any data or refs +slice.empty(); + +// Create a cell with bits and references +let cell_with_data_and_refs: Cell = beginCell(). + storeUint(42, 8). + storeRef(emptyCell()). + endCell(); +// Change `cell` type to slice with `begin_parse()` +let slice: Slice = cell_with_data_and_refs.asSlice(); +// Returns `false`, because `slice` has both data and refs +slice.empty(); +``` + + + + **Useful links:**\ + [`empty()` in Language→Reference](/language/ref/cells#sliceempty)\ + [`dataEmpty()` in Language→Reference](/language/ref/cells#slicedataempty)\ + [`refsEmpty()` in Language→Reference](/language/ref/cells#slicerefsempty)\ + [`emptyCell()` in Language→Reference](/language/ref/cells#emptycell)\ + [`beginCell()` in Language→Reference](/language/ref/cells#begincell)\ + [`endCell()` in Language→Reference](/language/ref/cells#builderendcell) + + + +### How to determine if Cells are equal + +We can easily determine Cell equality based on their hash. + +```tact +let a: Cell = beginCell() + .storeUint(123, 16) + .endCell(); + +let b: Cell = beginCell() + .storeUint(123, 16) + .endCell(); + +let areCellsEqual: Bool = a.hash() == b.hash(); // true +``` + + + + **Useful links:**\ + [`Cell.hash` in Language→Reference](/language/ref/cells#cellhash) + + + +## Sending messages + +### How to send a simple message + +```tact +send(SendParameters{ + to: destinationAddress, + value: ton("0.01"), // attached amount of Tons to send + body: "Hello from Tact!".asComment() // comment (optional) +}); +``` + +### How to send a message with the entire balance + +If we need to send the whole balance of the smart contract, then we should use the `SendRemainingBalance` send mode. Alternatively, we can use `mode 128`, which has the same meaning. + +```tact +send(SendParameters{ + to: ctx.sender, + value: 0, + mode: SendRemainingBalance, // or mode: 128 + bounce: true +}); +``` + + + + **Useful links:**\ + ["Sending messages" in the Book](/book/send#send-message)\ + ["Message `mode`" in the Book](/book/message-mode) + + + +### How to convert String to Int + +```tact +// Dangerously casts string as slice for parsing. Use it only if you know what you are doing. +// Try to parse the string as an slice +let string: Slice = "26052021".asSlice(); + +// A variable to store the number +let number: Int = 0; + +while (!string.empty()) { // A loop until slice has bytes + let char: Int = string.loadUint(8); // load slice bytes + number = (number * 10) + (char - 48); // we use ASCII table to get number +} + +dump(number); +``` + +### How to convert Int to String + +```tact +let number: Int = 261119911; + +// Converting the [number] to String +let numberString: String = number.toString(); + +// Converting the [number] to Float String +// passed argument `3` is is a exponentiation parameter of expression 10^(-3) that will be used for computing float number. This parameter required to be `0 <= digits < 77` +let floatString: String = number.toFloatString(3); + +// Converting the [number] as coins to human readable Strign +let coinsString: String = number.toCoinsString(); + +dump(numberString); // "261119911" +dump(floatString); // "261119.911" +dump(coinsString); // "0.261119911" +``` + + + + **Useful links:**\ + [`Int.toString` in Language→Reference](/language/ref/strings#inttostring)\ + [`Int.toFloatString` in Language→Reference](/language/ref/strings#inttofloatstring)\ + [`Int.toCoinsString` in Language→Reference](/language/ref/strings#inttocoinsstring) + + + +### How to get current time + +Use the `now()` method to obtain the current standard [Unix time](https://en.wikipedia.org/wiki/Unix_time). + +If you need to store the time in state or encode it in a message, use `Int as uint32`. + +```tact +let currentTime: Int = now(); + +if (currentTime > 1672080143) { + // do something +} +``` + + + + **Useful links:**\ + [`now()` in Language→Reference](/language/ref/common#now)\ + ["Current Time" in Tact-By-Example](https://tact-by-example.org/04-current-time) + + + +### How to generate a random number + +```tact +// Declare a variable to store the random number +let number: Int; + +// Generate a new random number, which is an unsigned 256-bit integer +number = randomInt(); + +// Generate a random number between 1 and 12 +number = random(1, 12); +``` + + + + **Useful links:**\ + [`randomInt()` in Language→Reference](/language/ref/random#randomInt)\ + [`random()` in Language→Reference](/language/ref/random#random) + + + +### How to throw errors + +The throw function in a contract is useful when we don't know how often to perform a specific action. + +It allows intentional exception or error handling, which leads to the termination of the current transaction and reverts any state changes made during that transaction. + +```tact +let number: Int = 198; + +// the error will be triggered anyway +throw(36); + +// the error will be triggered only if the number is greater than 50 +nativeThrowWhen(35, number > 50); + +// the error will be triggered only if the number is NOT EQUAL to 198 +nativeThrowUnless(39, number == 198); +``` + + + + **Useful links:**\ + [`throw()` in Language→Reference](/language/ref/advanced#throw)\ + [Errors in Tact-By-Example](https://tact-by-example.org/03-errors) + + + +### How to send a message with a long text comment + +If we need to send a message with a lengthy text comment, we should create a String that consists of more than `127` characters. To do this, we can utilize the StringBuilder primitive type and its methods called `beginComment()` and `append()`. Prior to sending, we should convert this string into a cell using the `toCell()` method. + +```tact +let comment: StringBuilder = beginComment(); +let longString = "..."; // Some string with more than 127 characters. +str_builder.append(longString); + +send(SendParameters{ + to: ctx.sender, + value: 0, + mode: SendIgnoreErrors, + body: longString.toCell(), + bounce: true, +}); +``` + + + + **Useful links:**\ + ["Sending messages" in docs](/language/guides/send#send-message)\ + [`StringBuilder` in docs](/language/guides/types#primitive-types)\ + [`Cell` in Language→Reference](/language/ref/cells) + + + +### How to calculate NFT item address by its index + +For Tacts' example, you should have the Tact code of the NFT item contract, placed in the same file. You can use the function as you wish, both inside and outside of a contract. + +For FunCs' example, you should have the code of item contract as a Cell. The function can be used outside of a contract by changing `self.nftItemCode` with a preassigned `nftItemCode`. + +Tact: + +```tact + +get fun getNftItemInit(item_index: Int): StateInit { +// Arguments for NftItem may vary, depending on contract + return initOf NftItem(collectionAddress, item_index, self.owner_address, self.collection_content); +} + +let itemIndex: Int = 0; // put your index +let itemInit: StateInit = self.getNftItemInit(itemIndex); +let itemAddress: Address = contractAddress(nft_init); +``` + +FunC (may also vary depending on collection's `deploy_item()` function): + +```func +fun getNftItemInit(item_index: Int): StateInit { + let data: Cell = beginCell().storeUint(item_index,64).storeSlice(self.nFTContractAddress.asSlice()).endCell(); + let itemInit: StateInit = StateInit{ + data: data, + code: self.nftItemCode + }; + return itemInit; +} + +let itemIndex: Int = 0; // put your index +let itemAddress: Address = contractAddress(self.getNftItemInit(itemIndex)); +``` + + + + **Useful links:**\ + [`initOf()` in the Book](/book/statements#initof)\ + [`contractAddress()` in Language→Reference](/language/ref/common#contractaddress)\ + [Tact collection and item contracts example](https://github.com/howardpen9/nft-template-in-tact/blob/tutorial/sources/contract.tact)\ + [FunC collection and item contracts example](https://github.com/Cosmodude/TAP/tree/main/contracts) + + diff --git a/pages/book/index.mdx b/pages/book/index.mdx index f5389dda..6093a450 100644 --- a/pages/book/index.mdx +++ b/pages/book/index.mdx @@ -22,7 +22,7 @@ Here are it's main contents: ### Cheetsheats -[Cheatsheets](/book/cs/from-func) are quick rundowns on Tact syntax and idioms with comparison to other blockchain languages, such as FunC (also on TON) and Solidity (Ethereum blockchain). Use those to transition to Tact as swiftly as possible! +[Cheatsheets](/book/cs/from-func) are quick rundowns on Tact syntax and idioms with comparison to other blockchain languages, such as FunC (also on TON) and Solidity (Ethereum blockchain). Use those to transition to Tact as swiftly as possible. +### Cookbook + +[Cookbook](/book/cookbook) is a handy collection of everyday tasks (and solutions) every Tact developer faces during smart contract development. Use it to avoid re-inventing the wheel. + + + + + ### Book [**The Tact Book**](/book/types) is a cohesive and streamlined sequence of educational materials about Tact. In general, it assumes that you’re reading it in sequence from front to back. Later parts build on concepts in earlier parts, and earlier parts might not delve into details on a particular topic but will revisit the topic in a later part. diff --git a/pages/language/index.mdx b/pages/language/index.mdx index c322c497..154bb183 100644 --- a/pages/language/index.mdx +++ b/pages/language/index.mdx @@ -2,7 +2,7 @@ import { Cards, Steps } from 'nextra/components' -Welcome to the **Language** section of Tact documentation — a place for becoming more familiar with the language in much more depth, compared to the [Book](/book). +Welcome to the **Language** section of Tact documentation — a place for becoming more familiar with the language in much more depth compared to the [Book](/book). Here are it's main contents: @@ -10,7 +10,7 @@ Here are it's main contents: ### Reference -[Reference](/language/ref/common) is a comprehensive list of language syntax and semantics. More detailed than the [Book](/book) at the expense of no additional guidance — targeted at more experienced programmers! +[Reference](/language/ref/common) is a comprehensive list of language syntax and semantics. More detailed than the [Book](/book) at the expense of no additional guidance — targeted at more experienced programmers.