summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYash <yashgarg5101@gmail.com>2023-09-09 12:26:40 +0530
committerYash <yashgarg5101@gmail.com>2023-09-09 12:26:40 +0530
commite5d4bb7933ec6b24e5cbe5ef6582bf522042d7da (patch)
tree1c17d202c824be28906477e047412c858f448026
parentfe3d3fd1ef8f0394a96eba96ad237234f0e43d1e (diff)
Update Tact doc with all examples
-rw-r--r--tact.html.markdown391
1 files changed, 372 insertions, 19 deletions
diff --git a/tact.html.markdown b/tact.html.markdown
index cefbd1c5..2cb650ee 100644
--- a/tact.html.markdown
+++ b/tact.html.markdown
@@ -4,6 +4,7 @@ filename: tact.tc
contributors:
- ["Tal Kol", "https://www.orbs.com/"]
- ["Kirill Malev", "https://fslabs.io"]
+ - ["Yash Garg", "https://github.com/yash0501"]
---
Tact language is used to program smart contracts on the
@@ -39,8 +40,8 @@ This is a simple counter contract that allows users to increment its value.
This contract has a state variable `val` that persists between contract calls
- the counter value. When persisted, this variable is encoded as `uint32` -
- a 32-bit unsigned integer. Contracts pay rent in proportion to the amount
- of persistent space they consume, so compact representations are encouraged.
+ a 32-bit unsigned integer. Contracts pay rent in proportion to the amount
+ of persistent space they consume, so compact representations are encouraged.
State variables should be initialized in `init()` that runs on deployment of
the contract.
@@ -72,17 +73,17 @@ contract Counter {
// Tact allows to create a contract
// persistent state variable of type Int to hold the counter value
val: Int as uint32;
-
+
// initialize the state variable when contract is deployed
init() {
self.val = 0;
}
-
+
// handler for incoming increment messages that change the state
receive("increment") {
self.val = self.val + 1;
}
-
+
// read-only getter for querying the counter value
get fun value(): Int {
return self.val;
@@ -97,7 +98,7 @@ traits. One of the commonly used traits is `Deployable`. It implements a simple
receiver for the Deploy message which helps deploy contracts in a standard way.
All contracts are deployed by sending them a message. This can be any message,
-but best practice is to designate the special `Deploy`
+but best practice is to designate the special `Deploy`
message for this purpose.
This message has a single field, `queryId`, which is provided by the deployer
@@ -122,17 +123,17 @@ import "@stdlib/deploy";
// the Deployable trait adds a default receiver for the "Deploy" message
contract Counter with Deployable {
-
+
val: Int as uint32;
-
+
init() {
self.val = 0;
}
-
+
receive("increment") {
self.val = self.val + 1;
}
-
+
get fun value(): Int {
return self.val;
}
@@ -161,7 +162,7 @@ representations than 257-bit to reduce storage cost.
The persistent state size is specified in every declaration of
a state variable after the `as` keyword.
-Storing 1000 257-bit integers in state [costs](https://ton.org/docs/develop/smart-contracts/fees#how-to-calculate-fees) about
+Storing 1000 257-bit integers in state [costs](https://ton.org/docs/develop/smart-contracts/fees#how-to-calculate-fees) about
0.184 TON per year. Storing 1000 32-bit integers only costs
0.023 TON per year by comparison.
@@ -169,26 +170,26 @@ Storing 1000 257-bit integers in state [costs](https://ton.org/docs/develop/smar
import "@stdlib/deploy";
contract Integers with Deployable {
-
+
// contract persistent state variables
// integers can be persisted in state in various sizes
// range -2^256 to 2^256 - 1 (takes 257 bit = 32 bytes + 1 bit)
- i1: Int as int257 = 3001;
+ i1: Int as int257 = 3001;
i2: Int as uint256; // range 0 to 2^256 - 1 (takes 256 bit = 32 bytes)
// range -2^255 to 2^255 - 1 (takes 256 bit = 32 bytes)
i3: Int as int256 = 17;
i4: Int as uint128; // range 0 to 2^128 - 1 (takes 128 bit = 16 bytes)
// range -2^127 to 2^127 - 1 (takes 128 bit = 16 bytes)
- i5: Int as int128;
+ i5: Int as int128;
i6: Int as coins; // range 0 to 2^120 - 1 (takes 120 bit = 15 bytes)
// range 0 to 18,446,744,073,709,551,615 (takes 64 bit = 8 bytes)
- i7: Int as uint64 = 0x1c4a;
+ i7: Int as uint64 = 0x1c4a;
// range -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// (takes 64 bit = 8 bytes)
- i8: Int as int64 = -203;
+ i8: Int as int64 = -203;
i9: Int as uint32 = 0; // range 0 to 4,294,967,295 (takes 32 bit = 4 bytes)
// range -2,147,483,648 to 2,147,483,647 (takes 32 bit = 4 bytes)
- i10: Int as int32 = 0;
+ i10: Int as int32 = 0;
i11: Int as uint16 = 0; // range 0 to 65,535 (takes 16 bit = 2 bytes)
i12: Int as int16 = 0; // range -32,768 to 32,767 (takes 16 bit = 2 bytes)
i13: Int as uint8 = 0; // range 0 to 255 (takes 8 bit = 1 byte)
@@ -196,7 +197,7 @@ contract Integers with Deployable {
init() {
// we can define numbers in hex (base 16)
- self.i2 = 0x83dfd552e6372;
+ self.i2 = 0x83dfd552e6372;
self.i4 = 1507998500293440234999; // we can define numbers in decimal
self.i5 = pow(10, 9); // this is 10^9 = 1,000,000,000
self.i6 = ton("1.23"); // easy to read coin balances
@@ -220,19 +221,371 @@ contract Integers with Deployable {
}
```
+## Bools, Addresses, Strings, Operators and Constants
+
+### Bool
+
+Bool can be used for boolean variables
+
+```js
+b1: Bool = true;
+b2: Bool = false;
+```
+
+### Address
+
+Address is another primitive data type. It represents standard addresses on
+the TON blockchain.
+TON is divided into multiple chains called workchains. One of the internal
+fields of the address is the workchain id:
+0 - The standard workchain, for regular users. Your contracts will be here.
+-1 - The masterchain, usually for validators.
+
+```js
+// bouncable (same foundation wallet)
+a1: Address = address("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N");
+// non-bounceable (same foundation wallet)
+a2: Address = address("UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI");
+```
+
+### String
+
+Tact has basic support for strings. Strings support unicode and don't
+have any special escape characters like \n.
+Strings are immutable. Once a sequence of characters is created, this
+sequence cannot be modified.
+If you need to concatenate strings in run-time, you can use a StringBuilder.
+This object handles gas efficiently and supports append() of various types to
+the string.
+
+```js
+s1: String = "hello world";
+sb: StringBuilder = beginString();
+sb.append(self.s1);
+```
+
+### Integer Operations
+
+Addition, subtraction, multiplication, division, modulo,
+shift left and right, minimum and maximum numbers, absolute value
+
+```js
+i: Int = -12; // temporary variable, runtime Int type is always int257
+i = i1 * 3 + (i2 - i); // basic math expressions
+i = i1 % 10; // modulo (remainder after division), 3001 % 10 = 1
+i = i1 / 1000; // integer division (truncation toward zero), 3001 / 1000 = 3
+i = i1 >> 3; // shift right (multiply by 2^n)
+i = i1 << 2; // shift left (divide by 2^n)
+i = min(i2, 11); // minimum between two numbers
+i = max(i2, 66); // maximum between two numbers
+i = abs(-1 * i2); // absolute value
+```
+
+### Constants
+
+Unlike variables, constants cannot change. Their values are
+calculated in compile-time and cannot change during execution.
+
+```js
+const StateUnpaid: Int = 0;
+```
+
+## Getters, Receivers and Messages
+
+### Getters
+
+Getters are special contract functions that allow users to query
+information from the contract.
+Contract methods starting with the prefix get fun are all getters.
+Calling getters is free and does not cost gas.
+Getters are read-only, they cannot change the contract persistent state.
+A contract cannot execute a getter of another contract. Getters are only
+executable by end-users off-chain.
+
+```js
+count: Int as uint32 = 17;
+
+get fun counter(): Int {
+ return self.count;
+}
+```
+
+### Receivers
+
+Contract methods named receive() are the handlers that process
+each incoming message type.
+Tact will automatically route every incoming message to the correct receiver
+listening for it according to its type. A message is only handled by one receiver.
+
+Handler for "increment" textual message - this is a textual string message,
+these cannot carry input arguments
+
+```js
+receive("increment") {
+ self.val = self.val + 1;
+}
+```
+
+### Messages
+
+Messages are defined using the message keyword. They can carry input
+arguments. For integers, you must define the encoding size, just like in
+state variables.
+
+Handler for the "Add" message - this is a binary message that has an input
+argument (amount)
+
+```js
+receive(msg: Add) {
+ self.val = self.val + msg.amount;
+}
+```
+
+## Structs
+
+Structs allow you to combine multiple primitives together in a more semantic way.
+Structs can define complex data types that contain multiple fields of
+different types. They can also be nested.
+
+```js
+// Normal struct
+struct Point {
+ x: Int as int64;
+ y: Int as int64;
+}
+
+// Nested struct
+struct Params {
+ name: String = "Satoshi"; // default value
+ age: Int? = null; // optional field
+ point: Point; // nested structs
+}
+```
+
+## Message Sender and Throwing Errors
+
+### Message Sender
+
+Every incoming message is sent from some contract that has
+an address. You can query the address of the message sender by calling sender()
+
+```js
+deployer: Address = sender();
+```
+
+### Errors
+
+When an error is thrown, the transaction reverts. By writing a
+require() on a condition that isn't met
+
+```js
+require(self.val < 5, "Counter is too high");
+```
+
+## Messages Between Contracts, Sending and Receiving TON Coins
+
+### Messages Between Contracts
+
+Different contracts can only communicate with
+each other by sending each other messages.
+
+This example sends a message to the to address with value of 1 TON and body
+of a comment with a string "Hello, World!".
+SendIgnoreErrors means that even when error occurs during message sending
+next messages would be sent anyway.
+
+```js
+let to: Address = ...;
+let value: Int = ton("1");
+send(SendParameters{
+ to: to, // address of receiver
+ value: value, // amount of TON you want to send
+ mode: SendIgnoreErrors, // 8-bit flag configuring how to send message
+ bounce: true, // if set to true (default) then message
+ // will be bounced back to sender
+ body: "Hello, World!".asComment() // message body as Cell
+});
+```
+
+### Receiving TONs
+
+You can query the contract balance with myBalance() - note
+that the value is in nano-tons (like cents, just with 9 decimals). The balance
+already contains the incoming message value.
+You can also get the incoming TON balance with context().value
+
+```js
+val: Int as int64 = myBalance()
+// or
+// print how much TON coin were sent with this message
+dump(context().value);
+```
+
+### Sending TONs
+
+We can send any amount of TON to any address just like we created
+a send call between different contracts
+
+Send mode SendRemainingValue will add to the outgoing value any excess left
+from the incoming message after all gas costs are deducted from it.
+
+```js
+amount: Int as coins = ton("1");
+send(SendParameters{
+ to: sender(),
+ bounce: true,
+ value: amount,
+ mode: SendRemainingValue + SendIgnoreErrors
+});
+```
+
+## If/Else statements and Loops
+
+### If
+
+Tact supports if statements in a similar syntax to most programming
+languages. Curly braces are required.
+We can have the else and else if similar to other programming languages.
+
+```js
+if (val > 1000) {
+ dump("larger than 1000");
+} else if (val > 500) {
+ dump("between 500 and 1000");
+} else {
+ dump("smaller than 500");
+}
+```
+
+### Loops
+
+Tact does not support traditional 'for' loops, 'break' and 'continue'
+statements in loops.
+The repeat loop statement input number must fit within an int32.
+
+```js
+// repeat exactly 10 times
+
+repeat (10) {
+ i = i + 1;
+ sum = sum + i;
+}
+
+// While loop
+
+let x: Int = 10;
+while(x > 0) {
+ x = x - 1;
+}
+
+// do-until loop
+
+let x: Int = 10;
+do {
+ x = x - 1;
+} until (x <= 0);
+```
+
+## Functions
+
+Functions in Tact start with the fun keyword. Functions can receive multiple
+input arguments and can optionally return a single output value. You can
+return a struct if you want to return multiple values.
+
+```js
+fun average(a: Int, b: Int): Int {
+ return (a + b) / 2;
+}
+```
+
+## Maps and Arrays
+
+### Maps
+
+Maps are a dictionary type that can hold an arbitrary number of items,
+each under a different key.
+The keys in maps can either be an Int type or an Address type.
+You can check if a key is found in the map by calling the get() method.
+Replace the value under a key by calling the set() method.
+
+```js
+mi1: map<Int, TokenInfo>; // maps with Int as key
+ma1: map<Address, TokenInfo>; // maps with Address as key
+```
+
+### Arrays
+
+To create an array, define a map with 'Int' type as key as well as value.
+
+```js
+arr: map<Int, Int>; // this is our array implemented with a map
+```
+
+## Ownable Standard Library
+
+The Ownable trait allows the contract to set an owner role, which can have
+higher priviliges from everybody else.
+For this you would need to import the "@stdlib/ownable" library and inherit
+it in your contract
+
+- Use the self.requireOwner() call to verify that the person making that
+ function call is the owner of contract
+- 'ChangeOwner{newOwner: Address}' message which allows the owner to
+ transfer ownership.
+- Define state variables named 'owner: Address' and 'stopped: Bool' and
+ call 'self.requireNotStopped()' on actions that should be stopped.
+- Define state variables named 'owner: Address' and "stopped: Bool' and
+ call 'self.requireNotStopped()' on actions that should be stopped.
+
+```js
+import "@stdlib/ownable";
+import "@stdlib/deploy";
+
+contract Counter with Deployable, Ownable {
+ owner: Address;
+
+ init() { // initialize a contract with default values like 'constructor'
+ self.owner = sender(); // we can initialize owner to any value we want, the deployer in this case
+ self.val = 0;
+ }
+
+ // this message in only available to the owner
+ receive("double") {
+ self.requireOwner();
+ self.val = self.val * 2;
+ }
+
+ // this message will only work until the contract was stopped
+ receive("increment") {
+ self.requireNotStopped();
+ self.val = self.val + 1;
+ }
+
+ // this message will only work as long as the contract is not stopped
+ receive("increment2") {
+ self.requireNotStopped();
+ self.val = self.val + 1;
+ }
+}
+
+```
+
## Additional resources
- [TON Documentation](https://ton.org/docs/#/)
- [Tact Docs](https://docs.tact-lang.org/)
+- [Tact by Example](https://tact-by-example.org/)
- [Community portal](https://society.ton.org)
- [Blockchain portal](https://ton.org)
- [Stackoverflow](https://stackoverflow.com/questions/tagged/ton)
## Social
+
- [Tact community](https://t.me/tactlang)
- [Developer community](https://t.me/tondev_eng)
- [TON Learn](https://t.me/ton_learn)
- [Tondev News](https://t.me/tondevnews)
+- [TON Technical Updates](https://t.me/thetontech)
## Useful blogposts
@@ -244,7 +597,7 @@ contract Integers with Deployable {
- Add smart contracts examples
- Add more links to documentations
-This file is based on [Tact By Example](https://tact-by-example.org/04-decimal-point).
+This file is based on [Tact By Example](https://tact-by-example.org).
P.S. If by any chance you're familiar with [Forth](https://learnxinyminutes.com/docs/forth/),
you can also take a look at [Fift](https://ton-blockchain.github.io/docs/fiftbase.pdf).