You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
By combining both of these patterns you can achieve a comprehensive description of your smart-contract's systems ensuring secure interaction between them and unleash full potential of TON actors model.
151
155
152
-
###Implementation in Tact
156
+
## Implementation in Tact
153
157
154
158
Tact is a high-level programming language for the TON Blockchain, focused on efficiency and simplicity. It is designed to be easy to learn and use while being well-suited for smart contract development. Tact is a statically typed language with a simple syntax and a powerful type system.
155
159
156
160
:::info
157
161
For more details, refer to the [Tact documentation](https://docs.tact-lang.org/#start) and [Tact By Example](https://tact-by-example.org/00-hello-world).
158
162
:::
159
163
160
-
#### Creating a Tact contract
164
+
Let's create and modify our smart-contract folowing standard steps decsribed in previous [Blueprint SDK overview](/v3/guidelines/quick-start/developing-smart-contracts/blueprint-sdk-overview) section.
└─ HelloWorld.ts # Wrapper for HelloWorld contract
217
+
```
218
+
</TabItem>
219
+
</Tabs>
220
+
221
+
At the top of the generated contract file: `counter_internal.tolk`, you may see a [message](https://docs.tact-lang.org/book/structs-and-messages/) definition:
169
222
170
223
```tact
171
224
message Add {
@@ -308,7 +361,7 @@ Note, that the `owner` getter is automatically defined via the `Ownable` trait.
308
361
309
362
#### Complete contract
310
363
311
-
```tact
364
+
```tact title="CounterInternal.tact"
312
365
import "@stdlib/deploy";
313
366
import "@stdlib/ownable";
314
367
@@ -354,132 +407,34 @@ contract CounterInternal with Deployable, Ownable {
354
407
}
355
408
```
356
409
357
-
####Using contract wrappers
410
+
###Step2: Using contract wrappers
358
411
359
412
[Wrappers](https://docs.tact-lang.org/book/compile/#wrap-ts) facilitate contract interaction from TypeScript. Unlike in FunC or Tolk, they are generated automatically during the build process:
`External messages` are your main way of toggling smart contract logic from outside the blockchain. Usually, there is no need for implementation of them in smart contracts because in most cases you don't want external entry points to be accessible to anyone except you. If this is all functionality that you want from external section - standard way is to delegate this responsibility to separate actor - `wallet` which is practically the main reason they were designed for.
371
-
372
-
### Wallets
373
-
374
-
When we sent coins using wallet app in [getting started](/v3/guidelines/quick-start/getting-started#step-3-exploring-the-blockchain) section what `wallet app` actually performs is sending `external message` to your `wallet` smart contract which performs sending message to destination smart-contract address that you wrote in send menu. While most wallet apps during creation of wallet deploy most modern versions of wallet smart contracts - `v5`, providing more complex functionality, let's examine `recv_external` section of more basic one - `v3`:
375
-
376
-
```func
377
-
() recv_external(slice in_msg) impure {
378
-
var signature = in_msg~load_bits(512);
379
-
var cs = in_msg;
380
-
var (subwallet_id, msg_seqno) = (cs~load_uint(32), cs~load_uint(32));
381
-
var ds = get_data().begin_parse();
382
-
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
`External messages` are your only way of toggling smart contract logic from outside the blockchain. Usually, there is no need for implementation of them in smart contracts because in most cases you don't want external entry points to be accessible to anyone except you. If this is all functionality that you want from external section - standard way is to delegate this responsibility to separate actor - [wallet](v3/documentation/smart-contracts/contracts-specs/wallet-contracts#basic-wallets) which is practically the main reason they were designed for.
401
427
402
-
First thing to mention - is `signature` in message body and stored `public_key`. This refers to standard mechanism of asymmetric cryptography: during deployment process you create **private and public key pair**, store the second one in initial contract storage and then during sending external message through client **sign** it with **private key** attaching calculated `signature` to message body. Smart contract on its side checks if signature matches `public_key` and accepts external message if it is so.
428
+
Developing external endpoint includes several standard [approuches](/v3/documentation/smart-contracts/message-management/external-messages)and [security measures](/v3/guidelines/smart-contracts/security/overview) that might be overwhelming at this point. Therefore in this guide we will realize incrementing previously added to `hello_world`contract `ctxCounterExt` and add send message to out `Tact` contract.
403
429
404
-
Standard signature system for TON smart-contracts is `Ed25519` which is directly provided by `TVM` instruction `check_signature()`, but you can always implement another preferred algorithm by yourself.
405
-
406
-
:::tip
407
-
When you entered **magic 24 secret words** (i.e. **mnemonic phrase**) during [wallet creation](/v3/documentation/data-formats/tlb/tl-b-language) in your app what is practically performed is concatenation of those words into one string and hashing it to create your **private key**. So remember not to show them to anyone.
430
+
:::danger
431
+
This implementation is **unsafe** and may lead to loosing your contract funds. Don't deploy it to `mainnet`, especially with high smart-contract balance.
408
432
:::
409
433
410
-
Second thing is `seqno` (sequential number) as you can see this is practically just a counter that increments each time wallet smart-contract receives external message, but why do we need one?
411
-
The reason behind that lies in blockchain nature: since all transactions are visible to anyone, potential malefactor could repeatedly send already signed transaction to your smart contract.
412
-
413
-
> **Simpliest scenario:** you transfer some amount of funds to receiver, receiver examines transaction and sends it to your contract repeatedly until you run out of funds and receiver gains almost all of them.
414
-
415
-
Third thing is `subwallet_id` that just checks equality to the stored one, we will discuss its meaning a little bit later in internal messages section.
434
+
## Implementation
416
435
417
-
### Implementation
418
436
419
-
At this point reasons behind changes that we made to our counter in previous storage and get methods [section](/v3/guidelines/quick-start/developing-smart-contracts/storage-and-get-methods#smart-contract-storage-operations) should start to be more clear! We already prepared our storage to contain `seqno`, `public_key` and `ctx_id` which will serve same task as `subwallet_id` so let's adapt wallet's `recv_external` function to our project:
420
-
421
-
<TabsgroupId="language">
422
-
<TabItemvalue="FunC"label="FunC">
423
-
```func
424
-
() recv_external(slice in_msg) impure {
425
-
;; retrives validating data from message body
426
-
var signature = in_msg~load_bits(512);
427
-
var cs = in_msg;
428
-
var (ctx_id, msg_seqno) = (cs~load_uint(32), cs~load_uint(32));
429
-
430
-
;; retrieves stored data for validation checks
431
-
var (stored_id, stored_seqno, public_key) = load_data();
432
-
;; replay protection mechanism through seqno chack and incrementing
433
-
throw_unless(33, msg_seqno == stored_seqno);
434
-
;; id field for multiply addresess with same private_key
Copy file name to clipboardExpand all lines: docs/v3/guidelines/quick-start/developing-smart-contracts/storage-and-get-methods.mdx
+15-15Lines changed: 15 additions & 15 deletions
Original file line number
Diff line number
Diff line change
@@ -42,15 +42,15 @@ interface Cell {
42
42
43
43
## Implementation
44
44
45
-
Let's try to modify our smart-contract folowing standard steps decsribed in previous [Blueprint SDK overview](/v3/guidelines/quick-start/developing-smart-contracts/storage-and-get-methods) section.
45
+
Let's try to modify our smart-contract folowing standard steps decsribed in previous [Blueprint SDK overview](/v3/guidelines/quick-start/developing-smart-contracts/blueprint-sdk-overview) section.
46
46
47
47
### Step1: Edit smart-contract code
48
48
49
49
In case it's inconvenient to always serialize and deserialize storage cell, there is a pretty standard practice to define two wrapper methods that provide corresponding logic. If you didn't change smart-contract code it should contain following lines:
50
50
51
51
<TabsgroupId="language">
52
52
<TabItemvalue="FunC"label="FunC">
53
-
```func hello_world.fc
53
+
```functitle="hello_world.fc"
54
54
global int ctx_id;
55
55
global int ctx_counter;
56
56
@@ -76,7 +76,7 @@ global int ctx_counter;
76
76
```
77
77
</TabItem>
78
78
<TabItemvalue="Tolk"label="Tolk">
79
-
```tolk
79
+
```tolk title="hello_world.tolk"
80
80
global ctxID: int;
81
81
global ctxCounter: int;
82
82
@@ -110,7 +110,7 @@ Result of our modifications should look like this:
110
110
111
111
<TabsgroupId="language">
112
112
<TabItemvalue="FunC"label="FunC">
113
-
```func
113
+
```func title="hello_world.fc"
114
114
;; load_data retrieves variables from TVM storage cell
115
115
(int, int, int) load_data() {
116
116
var ds = get_data().begin_parse();
@@ -140,7 +140,7 @@ Result of our modifications should look like this:
140
140
```
141
141
</TabItem>
142
142
<TabItemvalue="Tolk"label="Tolk">
143
-
```tolk
143
+
```tolk title="hello_world.tolk"
144
144
// load_data retrieves variables from TVM storage cell
145
145
// impure because of writting into global variables
146
146
fun loadData(): (int, int, int) {
@@ -175,13 +175,13 @@ Don't forget to delete global variables `ctx_id`, `ctx_counter` and modify usage
175
175
176
176
<TabsgroupId="language">
177
177
<TabItemvalue="FunC"label="FunC">
178
-
```func
178
+
```func title="hello_world.fc"
179
179
var (ctx_id, ctxCounter, ctxCounterExt) = load_data();
180
180
save_data(ctx_id, ctxCounter, ctxCounterExt);
181
181
```
182
182
</TabItem>
183
183
<TabItemvalue="Tolk"label="Tolk">
184
-
```tolk
184
+
```tolk title="hello_world.tolk"
185
185
var (ctx_id, ctxCounter, ctxCounterExt) = load_data();
186
186
save_data(ctx_id, ctxCounter, ctxCounterExt);
187
187
```
@@ -194,15 +194,15 @@ The primary use of get methods is reading our storage data from outside the bloc
194
194
195
195
<TabsgroupId="language">
196
196
<TabItemvalue="FunC"label="FunC">
197
-
```func
197
+
```func title="hello_world.fc"
198
198
(int, int) get_counters() method_id {
199
199
var (_, ctxCounter, ctxCounterExt) = load_data();
200
200
return (ctxCounter, ctxCounterExt);
201
201
}
202
202
```
203
203
</TabItem>
204
204
<TabItemvalue="Tolk"label="Tolk">
205
-
```tolk
205
+
```tolk title="hello_world.tolk"
206
206
get get_counters(): (int, int) {
207
207
var (_, ctxCounter, ctxCounterExt) = load_data();
208
208
return (ctxCounter, ctxCounterExt);
@@ -219,13 +219,13 @@ npm run build
219
219
220
220
And that's it! In practice all get methods follow this simple flow and don't require anything more. Note that you can omit values returned from functions using '_' syntax.
221
221
222
-
## Step2: Updating wrapper
222
+
###Step2: Updating wrapper
223
223
224
224
Now lets update our wrapper class corresponding to new storage layout and new `get method`.
225
225
226
226
First, let's modify `helloWorldConfigToCell` function and `HelloWorldConfig` type to properly initialize our storage during deployment:
227
227
228
-
```typescript
228
+
```typescript title="HelloWorld.ts"
229
229
exporttypeHelloWorldConfig= {
230
230
id:number;
231
231
ctxCounter:number;
@@ -242,7 +242,7 @@ export function helloWorldConfigToCell(config: HelloWorldConfig): Cell {
242
242
```
243
243
Second, add a method to perform a request for the newly created get method:
244
244
245
-
```typescript
245
+
```typescript title="HelloWorld.ts"
246
246
asyncgetSeqnoPKey(provider: ContractProvider) {
247
247
const result =awaitprovider.get('get_counters', []);
And finally, let's write a simple test, checking that the deployment process initializes smart contract storage and correctly retrieves its data by get methods.
258
258
259
259
First, let's update the `before each` section and particularly the `openContract` logic with the following one:
0 commit comments