💎 欢迎来到第四章。 在本章中,我们将介绍智能合约开发和部署的各个方面。首先,我们将探讨如何添加更复杂的逻辑来增强智能合约的功能。接下来,我们将重点确保我们的测试得到更新和修复,以适应最新的变化。此外,我们还将深入研究如何实现资金提取和存入的高级逻辑。此外,我们还将处理现有合约的初始化过程,并纳入广泛的测试覆盖范围。此外,我们还将指导您使用 Blueprint 将合约部署到主网。最后,我们将让你掌握有效验证合约代码的必要知识。在本章中,这些课程将让你全面了解智能合约开发,确保你获得成功部署和验证所需的技能。 欢迎阅读第 4 章。在本章中,我们将逐步为我们研究的智能合约创建更复杂的逻辑,在 TypeScript 的帮助下理解它并编写本地测试。
8 R4 J, I. Z6 d& R+ ~$ y2 _让我们回到上一章中的合约代码。我们将继续在此合约的基础上进行构建。
- O$ u( w/ e( ^, ~3 h9 X" i3 |- #include "imports/stdlib.fc";
9 a5 }" I/ x" \' d8 I - ( u0 e9 m1 ?+ B F- {# m/ o
- () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {, [5 f8 D4 ?( `
- slice cs = in_msg.begin_parse();8 U: r. X# \( h( p0 Y
- int flags = cs~load_uint(4);( V& u# s. ? E& S0 w, g/ q
- slice sender_address = cs~load_msg_addr();
M1 @* M3 B$ c+ H/ D7 d - ) a. F0 w9 W* v( Q3 S- Z6 ^
- set_data(begin_cell().store_slice(sender_address).end_cell());
4 T( a1 y/ e* C9 ?$ A3 i6 U* c - }
2 x( [7 s4 q6 O! t0 ^; H z/ l! t$ d - / n4 x; d( O' k
- slice get_the_latest_sender() method_id {
: s! L* x, \/ B b. ~* k - slice ds = get_data().begin_parse();
; x- t e- s9 S" y6 Q0 U; q - return ds~load_msg_addr();
; K, |& G6 h6 D' h - }
复制代码让我再详细说明一下我们这节课的计划。我们将重写合约,使其具有以下逻辑: 它将收到一条带有特定命令的消息(命令用于确定应根据消息中收到的数据执行合约中的哪部分功能) 根据这条特定命令,它将增加存储在 c4 存储器中的数字。 它提供了一个 getter 方法,用于返回当前计数器的值和发送递增操作代码消息的地址。
6 ?; X# S0 l D) g- O$ Z
' ^$ @0 |; U) y% `- V/ D+ O* [! J5 j; l, Y- e
3 N9 P/ c( v/ Y( S% _& ?0 E8 W2 t$ j& Y$ H- P. F
我们已经熟悉了写入数据存储和 getter 方法的概念,因此这不会成为问题,反而是对我们技能的很好练习。 不过,命令的概念是新的。命令也被称为操作码,简称OPEC。操作码通常存储在传入 recv_internal 函数的 in_msg_body 片段的开头。操作码只是一个整数,我们用它来表示某个逻辑块。这是一个标准,但操作码本身并不会出现在这里--在发送信息之前,您必须在编写信息时输入操作码。 需要注意的是,当我们读取数据时,必须按照写入数据的顺序进行。为了确保 get_data() 在第一次运行时正常工作,我们将回到初始数据的概念,因此我们将从部署合约的那一刻起就在 c4 存储中存储某个值。 操作码正如我之前提到的,我们要把操作码放在 in_msg_body 的开头,所以让我们编写从这里读取操作码的代码: - #include "imports/stdlib.fc";+ A+ U9 \1 E7 J" y# e: E5 w
- 0 T2 u/ z( l0 }. Z
- () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {9 {. A0 b; A' F- H8 n
- slice cs = in_msg.begin_parse();
! C. U% v" [1 x" t r - int flags = cs~load_uint(4);
: @( H; X& L1 W0 \( B - slice sender_address = cs~load_msg_addr();9 r/ h0 U1 M5 a/ s: E, Z+ x
-
# y( e3 N0 p/ o: n$ X: d2 ^4 ~/ h - int op = in_msg_body~load_uint(32);
/ a% \: g6 `5 ^5 ^
1 h6 j! X# o* R- p7 v6 r- set_data(begin_cell().store_slice(sender_address).end_cell());. h$ N# E( Y. Q
- }
) V! U: v4 k6 y2 K3 p: n - $ k# l& ^5 t7 s( i
- slice get_the_latest_sender() method_id {
! K3 l" M2 l+ w8 R2 n: n - slice ds = get_data().begin_parse();
# L% p u5 Y k2 E' C1 e8 |: g, \ - return ds~load_msg_addr();
& n- ~4 u1 S/ J# F* g! y9 h - }
复制代码在 FunC 中,我们必须为某些变量指定内存中分配用于存储变量的位数。这就是为什么我们要在 ~load_uint(32) 方法中设置 32,这意味着这个整数的最大位数将是 32 位。 我们要用来递增计数器值的操作码将是 1。就这么简单。让我们将逻辑封装到 if 语句中,检查操作码是否等于 1: - () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {! \- G I1 [( x9 [% W$ e: T1 b; n# `
- slice cs = in_msg.begin_parse();
6 j+ i8 c) ~, d S& s - int flags = cs~load_uint(4);# _' s4 X3 q$ w9 F8 a' k
- slice sender_address = cs~load_msg_addr();! D2 O1 o: ]+ e7 W7 _% H8 N8 T
- : g0 j& }8 _! Q& j& J5 b- J
- int op = in_msg_body~load_uint(32);9 ~6 d7 C- d3 d2 c& w( i
-
1 ^% f5 g' @ r; Z# B - if (op == 1) {7 O; l( i" l6 I
- ;; counter logic is coming here 5 a" A# J; d1 i* l5 H9 G$ Z
- set_data(begin_cell().store_slice(sender_address).end_cell());
9 P( u6 U5 }- E' x3 H: o# M# [# R - }& ]5 F! T& O/ d8 V" k4 `
- }
0 k0 e" e' m0 G2 R" |3 e; d
2 U C( V2 @* U: z9 ?% r2 e- A6 {- slice get_the_latest_sener() method_id {, h& q! C. H# p {- \
- slice ds = get_data().begin_parse();
0 _6 \7 j9 d @/ T - return ds~load_msg_addr();
! F) m4 H6 J! n: n: t - }
复制代码但现在我们还需要更新 getter 方法,使其也能以正确的顺序读取存储数据。我们还要将其重命名为 get_contract_storage_data 因为现在它还提供计数器值和最新的发送者地址: ! {7 A# y' Q2 d
- (int, slice) get_contract_storage_data() method_id {
+ y' A$ p2 W( n$ k - slice ds = get_data().begin_parse();, b4 [, a6 K2 _9 D3 \3 @! P
- return (
2 q; p. g* N) c! s/ k8 H0 g9 w+ [ - ds~load_uint(32), ;; counter_value% N0 ]9 |% u% c0 N
- ds~load_msg_addr() ;; the most recent sender" k2 x, C& G9 T6 v! u
- );
' k, `+ d! f( B0 ]8 c - }
复制代码您可以注意到,由于我们现在返回的值不止一个,因此我们还将函数定义中的预期结果和返回结果都用圆括号封装起来。 让我们看看最终的代码是怎样的: - #include "imports/stdlib.fc";5 y S J7 J: A+ r( s& [ q
- # j+ R0 }/ K: ?+ [6 h, ]+ \7 y
- () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {! v) }# |1 D' ]8 Q
- slice cs = in_msg.begin_parse();
9 c1 K' X* T( L9 D - int flags = cs~load_uint(4);
$ |# M* }' T* K2 h6 @+ w3 d; @9 r - slice sender_address = cs~load_msg_addr();
& ?# y/ ] x1 c" @1 J# f( @& r - / {; B. t; S( E' W* C
- int op = in_msg_body~load_uint(32);
1 |& F8 [. G4 H) _, { -
8 J7 c5 F) b9 `! r7 o6 J0 ^! O0 @ - if (op == 1) {! i: \) J' M, m, P% z7 L+ E
- slice ds = get_data().begin_parse();
3 V- b3 L/ O8 L5 \% } - int counter_value = ds~load_uint(32); 2 G9 {' w( E0 m6 g) Y' y
- set_data(
T, p0 u8 W* i' I - begin_cell().store_uint(counter_value + 1, 32).store_slice(sender_address).end_cell()4 Z* ]- _9 q4 U, u P# W( n
- );
* }9 I$ N1 M# @- l9 O/ p - return ();
! ?# U; I+ b* J1 A& x - }
_8 y7 x, r$ d; c/ @2 n
5 }9 b0 ~5 c: R+ ?# h7 s- return (); T, ^3 v3 P/ F$ B9 z
- }- j" S9 E' h6 _0 ?
: ~8 H' [* t( @2 r& `) s% ]- (int, slice) get_contract_storage_data() method_id {
- V. G6 B( s9 ^9 z8 [ - slice ds = get_data().begin_parse();" Y5 h$ W; j# Q
- return (
& N. X: x! a+ p) d$ e - ds~load_uint(32), ;; counter_value6 e" B1 a2 k/ x6 h
- ds~load_msg_addr() ;; the most recent sender J/ n7 w7 X: _3 V5 r/ J
- );, d+ f8 x; t6 c: T4 |4 \
- }
复制代码您只需运行 yarn compile. 在下一课中,我们将更新智能合约的测试,这样它就能帮助我们验证合约的行为。 # r, S" ~; K" W7 m/ f; t5 \
( N" }) Y/ }! g5 c2 R" p1 Z' T4 i& B& g5 x# ~# @' r a4 ]9 X
# ~8 C8 ]" ?' K: U. r |