Giới thiệu
Xin chào, hôm nay chúng ta sẽ review 2 contract thay vì chỉ 1. Bao gồm những contract liên quan tới cross-contract calls (gọi contract chéo), promise và cách chúng hoạt động trên NEAR. Nếu bạn muốn tìm hiểu thêm về cách kết hợp các kiến thức về NEAR-SDK thì đây là một sự khởi đầu tốt. Contract đầu tiên chúng ta cùng review có tên là whitelist contract, và nó được sử dụng trên mainnet để whitelist các staking pool. Việc này quan trọng vì nó giúp quá trình phân phối token được thực hiện thông qua các smart contract. Chúng ta sử dụng các lockup contract (contract khóa) sẽ được review ở serie này, contract khóa này là một smart contract độc lập, lưu trữ và khóa các token, đồng thời release chúng trong các khoảng thời gian nhất định. Ví dụ các token được phát hành trong hơn 2 năm và được phân bổ tuyến tính mỗi block. Những gì ta muốn làm là cung cấp khả năng stake token, bao gồm cả những token chưa được release. Điều này có nghĩa là bạn sẽ có thể delegate (ủy quyền) tất cả token mà bạn đã lock trong 2 năm (ví dụ thế) và bắt đầu kiếm được phần thưởng từ việc này. Tất cả được thực hiện thông qua smart contract, lockup contract sẽ gọi tới staking pool contract (đã review trước đây), và sau đó chuyển token từ contract này tới contract khác.
Các token từ lockup contract rút ra khỏi một account và chuyển tới account của staking pool. Nếu một staking pool không đảm bảo được những yêu cầu về việc bảo vệ tài sản, chẳng hạn như khả năng trả lại token, thì sẽ có những vấn đề nảy sinh. Giả sử ta xây dựng một staking pool không chỉ cho phép stake token mà còn cho phép rút luôn token đã stake tới bất kỳ account nào. Hành động này cho phép bạn rút tài sản về ví trước khi thời gian release token của staking pool kết thúc và đây là một hành vi nguy hiểm.
Đây là lý do whitelist contract ra đời, nơi các triển khai tùy chỉnh của staking pool được NEAR Foundation phê duyệt, có thể được sử dụng bởi các lockup contract. Đồng thời, ta cũng muốn có khả năng tạo mới các staking pool được chấp thuận bởi bất kỳ ai mà không cần thông qua NEAR Foundation. Điều này cho phép ai cũng tạo được một staking pool thông qua staking pool factory (đây là contract thứ 2 chúng ta sẽ review hôm nay). Cách hoạt động của nó là khi một lockup contract muốn được ủy quyền, trước khi có thể chuyển token sang contract này, người dùng cần chọn một staking pool trước. Khi chọn staking pool rồi, lockup contract sẽ đưa ra một transaction để kiểm tra account id có trong whitelist contract không, nếu có thì account id đó được whitelist, sau đó lockup contract có thể tiếp tục với quá trình ủy quyền. Nó cho phép lockup contract thực sự chuyển tài sản sang contract whitelist. Staking pool contract có tính bảo mật cao hơn và cung cấp những API mà local contract yêu cầu, cụ thể sẽ không xảy ra trường hợp token của lockup contract bị đánh cắp hay token của chủ sở hữu bị khóa.
Việc này cũng rất quan trọng đối với lịch trình đầu tư của những nhân viên trong đội ngũ phát triển NEAR. Nó nằm trong lịch trình đầu tư 4 năm và điều đó cho phép NEAR Foundation phát hành transaction cụ thể cho người đó để unstake tất cả khỏi staking pool và trả lại số tiền đã đầu tư trở lại NEAR Foundation trong trường hợp nhân viên đó rời đi hoặc bị đuổi việc. Trên đây là những thông tin cơ bản về lockup và whitelist contract.
Mã nguồn của các contract này có thể tìm thấy trên Github của NEAR. Bài viết này dựa trên video gốc sau (khuyến khích bạn xem video này):
Whitelist Contract
Main Structure
Bây giờ chúng ta cùng phân tích whitelist contract. Đây là contract khá đơn giản và không có quá nhiều logic, chúng ta đã biết phần lớn nội dung chính của nó từ những bài review trước của series:
Contract này sử dụng NEAR API LookupSet. Tương tự như một unordered set. Nó là một persistent collection, nhưng không có iterator (đối tượng lặp), nên bạn không thể lặp (iterate) qua các key của những element trong set. Bạn chỉ có thể check nếu một phần tử đã cho có hay không, và bạn có thể thêm phần tử đó vào trong set. Bạn không thể xem những phần tử nào hiện diện trong tập hợp này. Bằng cách này sẽ cải thiện hiệu quả của việc lưu trữ và truy cập từ nhiều lần đọc đến một vài lần đọc. contract chứa một vài trường.
Contract có một số trường dữ liệu. Đầu tiên là foundation_account_id – Đây là account ID kiểm soát hành động whitelist các staking pool mới. Tài khoản này có thể đưa 1 staking pool hoặc các staking pool factory vào whitelist.
Factory là một contract có thể tạo ra một instance mới của struct staking pool. Cách nó hoạt động là khi bạn phát hành một giao dịch với staking factory đã được contract này đưa vào whitelist từ trước, nó sẽ tạo một account mới làm tài khoản phụ của factory. Trong trường hợp của chúng ta trên mainnet, tài khoản này được gọi là poolv1.near, là một staking pool factory mà NEAR Foundation sử dụng. Nó tạo ra một contract, ví dụ bisontrails.poolv1.near, trên đó triển khai code whitelist staking pool trong whitelist mà factory có thể tạo ra, (chúng ta sẽ đi vào phần staking pool factory sau) nhưng đồng thời nó cũng có thể liệt kê whitelist cho staking pool này. Đây là cách nó hoạt động. Hàm khởi tạo (constructor) chỉ nhận một tham số là foundation_account_id. Tài khoản của foundation có quyền cao hơn đối với contract:
Getters
Có rất nhiều getter ở đây
Bạn có thể check nếu một staking pool account được whitelist. Đây là cách mà lockup contract kiểm tra nếu một pool được whitelist. Cơ bản là chỉ check nếu một phần tử tồn tại trong set. Cách thứ hai có thể check là nếu factory được whitelist, trong case này việc này không cần thiết và không ai gọi tới contract này.
Add Staking Pool Method
Đây là một method có được thể call bởi cả factory lẫn NEAR Foundation. Những gì ta làm ở đây là kiểm tra staking method này có thêm staking_pool_account_id vào whitelist hay không. Nó kiểm tra xem id tài khoản có hợp lệ không, sau đó nếu pass,ta sẽ kiểm tra xem đây có phải là factory hay không. Ta check trong set nếu caller của method này có trong whitelist của các factory. Nếu nó được call staking pool factory thì oke. Nếu không thì nó phải là account id của foundation, nếu không contract sẽ fail và method trả về lỗi panic.
Nếu ta vượt qua bài check về permission thì ta chỉ cần thêm staking pool này vào whitelist. Hiện tại,ta chỉ có một triển khai cho staking pool (Implementation for staking pool) , nhưng về lý thuyết, ta có thể sửa đổi việc triển khai này khi ta giới thiệu method khác, ví dụ như slashing, và staking pool phải có sự cho phép cần thiết. Nó cần phải duy trì một số token tối thiểu. Có một số thay đổi khác mà ta cần sửa đổi các verified contract trong thời gian đầu tư của mọi người là 4 năm. Rất nhiều thứ có thể xảy ra xung quanh mạng và chúng ta cần có khả năng thay đổi các staking pool. Ví dụ, nếu logic staking pool thay đổi. Nó cho phép tạo ra một factory mới là một phiên bản tốt hơn hoặc hỗ trợ một cái gì đó mà trước đây không được hỗ trợ. Nó không cho phép thay đổi phí thưởng ngay lập tức, mà chỉ cho phép nó thay đổi sau khoảng thời gian chờ 7 ngày, hoặc một số ý tưởng sửa đổi khác.
Tiếp theo remove_staking_pool chỉ có thể được call bởi foundation account nên một staking pool chỉ có thể bị hủy bởi foundation account.
Ngoài ra, add_factory cũng chỉ có thể được call bởi foundation account. Cơ bản là nó chỉ thêm vào whitelist của các factory account.
Cuối cùng, method remove_factory cũng chỉ có thể được gọi bởi foundation account và được dùng để xóa factory_account_id. Giả sử factory đầu tiên bị hết hạn, về cơ bản, foundation account có thể xóa factory đó khỏi whitelist cũng như xóa tất cả các pool trước đó khỏi whitelist. Bây giờ, bạn sẽ không thể chọn staking pool trước từ lockup contract, và cuối cùng check lại là lệnh được call từ foundation.
Đây là contract rất đơn giản và nó chỉ hoạt động trên internal set và method true duy nhất có thể nhìn thấy bên ngoài là is_whitelisted. Nó khá đơn giản, chỉ bao gồm một số các setter và getter.
Tính bất biến (immutability) của Smart Contracts
Cách các smart contract thường hoạt động trên Ethereum và trên các blockchain nền tảng khác là chúng không tự thay đổi (immutable). Trên Ethereum, mọi contract là bất biến, các developer sử dụng proxy contract (contract ủy quyền) cho phép nâng cấp endpoint cho một số contract nhất định, việc nâng cấp endpoint này là rất quan trọng với các token. Các contract cốt lõi (core contracts) là hoàn toàn bất biến, ta sẽ triển khai một lần và có khả năng sau này không thể thay đổi được, bởi vì nếu muốn thay đổi, bạn sẽ phải thực hiện hard fork và phải thuyết phục tất cả các validator thực hiện code migration.
Điều này quan trọng vì việc kiểm soát được thực hiện ở tự động bởi smart contract thay vì bởi một số thực thể tập trung. Ví dụ: trong khi nền tảng vẫn duy trì quyền kiểm soát lớn đối với các staking pool bằng cách có khả năng xóa staking pool, nó không có quyền kiểm soát đưa vào blacklist một thực thể cụ thể trong thế giới thực khỏi việc triển khai staking pool.
Họ vẫn có thể tạo một nhóm staking pool mới với mức độ ẩn danh nhiều nhất có thể và tạo một staking pool mà không cần cấp phép để trở thành validator trên mainnet.
Một số thứ đến từ sự phân quyền, thứ khác đến từ sự hạn chế trong việc kiểm soát.
Mặc dù Foundation được cho là để hỗ trợ mạng lưới nhưng trong một số trường hợp, Foundation có thể bị buộc phải làm điều gì đó không tốt cho mạng. Giả sử chính phủ vào cuộc và cố gắng ép buộc. Nếu ta có ít khả năng thực hiện việc này hơn thì sẽ có khả năng bảo mật cao hơn trên mạng. Khi thiết kế contract, chúng tôi nghĩ “Giá trị trong contract này là bao nhiêu?” hoặc “Khả năng ảnh hưởng của nó đối với mạng nói chung là gì?”. Nếu đó là giá trị nhỏ thì bạn có thể giữ uy tín miễn là được cộng đồng tin tưởng, nếu đó là giá trị lớn thì không ổn tí nào.
Khi chúng ta thực sự đi sâu vào lockup contract, và cách nó được thiết kế, ví dụ: vesting, được thiết kế nhằm cho phép foundation rút số tiền vốn còn lại, nhưng đồng thời ngăn chặn foundation rút các khoản tiền đã được vested. Đây là một cách hoạt động hợp pháp ngoại trừ việc nó được mã hóa vào hợp đồng thông minh. Whitelist là một hợp đồng rất quan trọng, bởi vì trước khi các quỹ bị khóa, phần lớn các quỹ đều kiểm soát mạng lưới thông qua các local contract trong các staking pool thông qua whitelist cụ thể này, vì vậy điều quan trọng là phải thiết kế nó theo cách giữ được sự phân quyền và bảo mật mạng lưới mà không cần trao quyền kiểm soát cho foundation.
Giả sử có điều gì đó đã xảy ra và foundation bắt đầu có những hoạt động xấu nhưng bạn đã có thể tạo một nhóm staking pool mới thông qua factory và ủy quyền cho staking pool đó, lúc này foundation không thể ngăn bạn ủy quyền cho staking pool đó.
Staking Pool Factory Contract
Include Bytes Macro
Contract staking_pool_factory là một hợp đồng mà bên trong nó lại chứa mã nguồn của staking pool contract. Trong Rust, bạn có thể làm việc này thông qua macro include_bytes. Cơ bản là nó lấy một file cục bộ và nhúng file vào trong tệp nhị phân dưới dạng vector. Dẫn đến trong binary của WebAssembly này, ta có thể có một phần trong allocated memory, đại diện cho staking pool cụ thể này (staking_pool.wasm)
Structure
Đây là cấu trúc:
Một số thông tin về gas, ta sẽ quay lại vấn đề này sau:
Có reward_fee_fraction vừa được chép từ contract của staking pool mà ta đã review từ trước.
External Contracts
Có những tham số của staking pool mà contract cần, đây là những trait và những external contract, nên đây là high level interface ta sử dụng để gọi những external contracts.
/// External interface for the callbacks to self.
Ta có hai trait, trait đầu tiên gọi là ExtSelf, bởi vì nó đại diện cho contract của chúng ta và chứa callback tới method on_staking_pool_create.
Trait thứ 2 cho whitelist contract, nó có method add_staking_pool và được whitelist contract gọi tới:
Trait trong Rust giống khái niệm interface trong Java. Bên trên ta đã định nghĩa một interface của một remote contract.
Initialization
Cùng xem qua kịch bản: Khi một factory được tạo, ta xác minh rằng nó chưa được khởi chạy (init) và ta xác minh staking_pool_whitelist_account_id, account id của whitelist contract, trong quá trình khởi tạo StakingPoolFactory. Ta cần biết staking_pool_whitelist_account_id. Đây là nơi whitelist contract được deploy để đưa vào whitelist staking pool vừa mới tạo. Sau đó tạo một set của những account đã tạo ở cuối snippet này.
Main Method
Giờ thì staking pool factory đã được tạo, ví dụ là poolv1.near chẳng hạn, và foundation đã whitelist factory trên whitelist contract bằng cách tạo một transaction khác. Bây giờ staking pool factory được whitelist, nghĩa là nó có quyền whitelist các staking pool mà nó tạo ra. Vậy nên giờ tới phần Validator, họ muốn tạo staking pool cho riêng họ. Cách làm là họ gọi create_staking_pool, nó cần một loạt các tham số. Đầu tiên là prefix. Giả sử đó là bisontrails, không có sufix của account id hiện tại, và nó đến từ account trên NEAR.
Một account chỉ có thể tạo các sub account, nên staking pool factory tạo sub account dưới quyền của chính nó – sẽ là bisontrails.poolv1.near chẳng hạn owner_id là account id của chủ sở hữu của staking pool đã thảo luận trước đây. Cả ba mục này về cơ bản là các tham số mà bạn sẽ chuyển đến một staking pool khi lần đầu khởi tạo. Đó là một argument mà bạn có thể đặt ủy quyền với staking pool. Ví dụ, staking_pool_id có thể là bisontrails.near. stake_public_key có thể stake key từ những validator node đang chạy, và reward_fee_fraction có thể là 10%. Lưu ý rằng method này là payable , nghĩa là nó cần một khoản phí gửi đính kèm (attached deposit), vì việc đầu tiên nó sẽ check là “Bạn đã gửi đủ tiền chưa?” Khoản deposit bạn cần gắn vào là 30 NEAR, và cái này có rất nhiều số 0, vì nó ở dạng đơn vị là yoctorNEAR. Bạn cần gắn 30NEAR là chính vì cần phải cover state cho contract trên mỗi lần tạo staking pool mới. Hợp đồng này khá lớn, tốn 250 kb và cần ít nhất là 25 NEAR, nhưng cũng phải mất thêm một tí phí nữa để đảm bảo tránh trượt giá. Đây là một trong những case bạn phải có một khoản phí gửi kèm theo, vì bạn không thể gắn lượng gas nhiều như này vào trong transaction này. Ngoài ra, ta cũng không thể chuyển gas sang NEAR trong contract, vì vậy lý tưởng nhất là giữ nguyên gas cho việc tính toán như là read/write, cross contract calls. Số balance được sử dụng để lưu trữ và dùng để transfer. Trong trường hợp này, nó sẽ tạo một account mới, tạo account trên NEAR yêu cầu bạn phải trả phí lưu trữ account. Bộ nhớ trong trường hợp này sẽ không chỉ là account đó mà còn là contract của account.Và trong trường hợp này, đây là code của staking pool contract.
Tiếp theo là xác minh prefix không chứa dấu chấm (.), nghĩa là bản thân account đó ko phải là một account phụ. Sau đó ta tạo mới staking_pool_account_id bằng cách nối id tài khoản với dấu (.) , qua đó xác minh rằng account id của staking pool là hợp lệ. Cơ bản là nếu tất cả các assert check này bị fail, NEAR sẽ refund lại số token. Nếu một transaction có attached deposit bị fail, số tiền đó sẽ trở lại cho sender hoặc predecessor, bởi vì chỉ predecessor có thể attach balance. Việc thực hiện một loạt các asserts check ở đây giúp an toàn hơn. Tiếp theo, ta xác minh rằng owner_id của staking pool là hợp lệ. Về cơ bản, đây là một loạt các helper cũng được xác minh trên staking pool, giúp đảm bảo rằng nếu bạn không truyền đủ tham số, hoặc truyền hơi sai một tí thì bạn sẽ nhận được báo lỗi trước khi transaction được thực hiện để tránh bị đốt gas và tránh bị lock token.
Cuối cùng, ta kiểm tra sử dụng insert để đảm bảo rằng staking pool không tồn tại. Cơ bản insert sẽ trả về true nếu đó là một element mới duy nhất và trả về false nếu nó đã tồn tại trong set. Đây là cách Rust hashset hoạt động giống như cách hoạt động của ordered set. Vì vậy, nếu tên pool đã tồn tại, ta sẽ không thêm staking pool hoặc cố gắng tạo lại account này. Insert thực hiện hai việc, nó element vào trong storage, cũng như trả về true nếu phần tử là duy nhất và không tồn tại trước đó hoặc trả về false nếu phần tử đã tồn tại. Nếu set không có giá trị này thì true được trả về, nếu set có element này thì trả về false.
Cuối cùng, ta dùng medium level API, không sử dụng raw cost method cũng không dùng high level interface. Cách hoạt động là ta tạo một promise mới, promise sẽ tạo một temp struct ở trong NEAR SDK, và nó nhớ được receiver của promise này. Có thể nghĩ rằng như kiểu contract sẽ đưa ra giao dịch đối với account id đó. Ta sẽ gọi là staking pool account id không tồn tại. Tất nhiên đó không phải là một transaction, mà là một receipt thì hơn. Tiếp theo là hành động đầu tiên cho promise này. Ta bắt đầu thực hiện chuỗi các hành động theo promise này. Đầu tiên là create_account – tạo một account mới hoặc fail nếu account đã tồn tại. Sau đó, deposit balance vào, gửi toàn bộ số đã chuyển mình vì ta không giữ nó trong factory, số tiền đó sẽ đi kèm với một receipt trên remote account. Như đã giải thích trước đó, include_bytes là một macro tạo một static slide đổi thành một vector sẽ được chuyển cho một hành động deploy. Nó sẽ giúp deploy code trên remote account. Bạn chỉ có thể deploy code trên những account bạn kiểm soát, create_account cho bạn quyền như thể bạn là owner của account chỉ transaction cụ thể này. Bạn có thể sử dụng method deploy_contract, có thể stake và làm một số việc khác thay mặt cho contract này trong transaction đầu tiên. Cuối cùng, ta khởi tạo contract staking pool sử dụng serda API. Ta sử dụng struct này và serialize nó sang dạng JSON, method tên là new. Tham số đầu tiên là attached deposit tới function call. Ta không cần nó. Tiếp theo là lượng gas bạn có thể lấy từ số gas hiện tại của mình và ngay lập tức lấy ra sau khi gas đó đi tới promise. Ví dụ method call hết 100 tera gas (tera gas là đơn vị gần như con người có thể hiểu được)
Bạn có 100 tera gas ban đầu, và ta truyền 25 terra gas ( ở const BASE ), con số này x2. Ngay lập tức truyền 50 terra gas tới function call của method này, truyền 50 terra gas nghĩa là mình chỉ còn ít hơn 50 terra gas, bởi vì ta đã đốt mất một ít từ những lần xử lý logic trước đó. Cộng thêm mỗi hành động đưa vào promise sẽ lại tốn thêm ít gas. Ví dụ, action deploy tốn phí để chuyển các bytes từ account này sang cái khác. Sau đó, tương tự như cách js hoạt động, nó gắn dependency vào promise trước. Đây là promise đầu tiên, và ta nói sau đó khi nó hoàn thành, hãy thực hiện tiếp promise thứ hai. Cách nó hoạt động như kiểu: bisontrails.near đã call contract này (poolv1.near) để tạo bisontrails.poolv1.near.
Đầu tiên ta tạo một promise tới bisontrails.poolv1.near và sau đó ta đính kèm một callback cho api này, điều này không tốt về mặt sử dụng các argument cho hai thứ khác nhau. Dù sao thì nó cũng call lại account id hiện tại. Vì vậy, sẽ gọi poolv1.near sau khi promise đầu tiên được thực thi. Cấu trúc: bisontrails.near gọi poolv1.near tạo một staking pool promise.Bây giờ tạo một promise cho bisontrails.poolv1.near và nó cũng tạo một promise cho chính nó trên method on_staking_pool.
Nó cần kết quả của method trước khi method bắt đầu, và nó chuyển ba đối số ở đây, bao gồm staking_pool_account_id, attached_deposit và predecessor_account_id. Vì vậy, nó là caller id, account nào được tạo và có bao nhiêu token attached trong trường hợp ta cần hoàn lại tiền. Bây giờ nếu bisontrails.poolv1.near thực thi thành công thì on_staking_pool_create sẽ nhận được kết quả thực thi. Giả sử có một số cấu hình sai khiến phương thức này cũng sẽ được gọi, nhưng sẽ nhận được một callback lại cho biết lệnh thất bại.Ta đã trả lại main promise ngay sau đó, có nghĩa là ta đã trả lại on_staing_pool_create trước. Quan trọng là vì kết quả của phương thức create_staking_pool phụ thuộc vào kết quả của promise on_staking_pool_create. Transaction không hoàn toàn bắt đầu song song, thay vào đó giờ đây nó phụ thuộc vào việc thực hiện method cụ thể này.
Callback
Xem phần callback nào:
Việc đầu tiên chúng ta làm là đảm bảo rằng promise này chỉ có thể được gọi bởi contract hiện tại bằng cách sử dụng assert_self.
Tiếp theo là sử dụng một utility helper method cho biết nếu dependency, việc tạo staking pool là thành công hay thất bại.
Ta làm điều này theo cách sau: Sử dụng 2 method, đầu tiên là check xem kết quả trả về là 1 – điều này là bất biến vì ta biết rằng không ai được phép gọi hai lần, method 2 là nếu kết quả trả về thành công. Method thực thi thành công thì trả về true, nếu không thì là false. Vì vậy biết được là staking pool đã được tạo ra hay chưa. Một lần nữa, ta cũng đã để 50 terra gas vào callback nên bây giờ ta lại đang ở trong callback này nên chỉ còn khoảng 50 terra gas.
Nếu thành công thì ta sẽ log lại rằng pool đã được tạo, sau đó cho nó vào whitelist. Sau đó ta gọi một promise khác từ callback, và gắn vào 25 terra gas. Giờ ta gọi tới staking_pool_whitelist_account_id(whitelist contract). Giờ ta có thể đưa vào whitelist staking pool vừa mới tạo, bởi vì ta đã truyền tham số này vào trong callback. Sau đó ta trả về một promise để tiếp tục quá trình, vì ta chỉ muốn hoàn thành toàn bộ giao dịch sau khi whitelist đã hoàn tất. Rust không có keyword return bởi vì nếu value cuối không có dấu chấm phẩy (;) thì nó sẽ hiểu đây là giá trị trả về. Nếu tạo không thành công, thì chỉ có một lý do, khoản tiền gửi chuyển vào staking pool để tạo sẽ được hoàn lại cho ta thay vì cho predecessor (người tiền nhiệm). Ta có 30 NEAR trên contact này và ta cần trả lại chúng cho sender, tránh bị lock. Đầu tiên ta làm là xóa nó khỏi list các staking pool đã được tạo, vì tạo bị thất bại. Ta nói rằng quá trình tạo đã fail và ta sẽ refund lại số tiền đã được đính kèm ( attached deposit). Bây giờ nó không phải là một khoản được đính kèm thực sự, bởi vì callback không nhận được số tiền đó.
Contract chuyển qua một refund receipt riêng, thường đến trước callback, nó lấy predecessor_account_id. Trong trường hợp này, nếu ta gọi predecessor_account_id, nó có nghĩa là hàm callback. Ta cần biết ai sẽ được trả lại lượng token đó, cách ta làm điều đó là tạo một promise sử dụng predecessor_account_id mới, và trả lại lượng token đã được gắn vào. Như có thể thấy, ta không return một promise, ta không quan tâm nếu nó thành công hay bị fail, việc trả về giá trị là false nghĩa là pool không được tạo. Thứ xảy ra bây giờ là transaction tiếp tục được thực hiện nhưng giá trị sẽ được trả lại cho frontend – NEAR CLI, nên bạn sẽ biết ngay lập tức là transaction đã bị failed. Bạn sẽ chưa lấy lại được tiền ngay, bạn sẽ cần đợi khoản tiền hoàn lại cụ thể này được thực thi trong block tiếp theo nhưng bạn biết rằng pool chưa được tạo nên bạn vẫn có thể tiếp tục. Đây là một ví dụ về cách bạn có thể thực hiện parallel promise (promise chạy song song).
Đây là cách hoạt động của staking pool factory. Đây là một getter kiểm tra xem có bao nhiêu staking pool đã được tạo ra có thể được gọi trên NEAR CLI:
Tổng kết
Tới đây là kết thúc buổi NEAR Live Conrtact Review | Phần 3: Whitelist và Staking Pool Factory.
Cảm ơn bạn đã dành thời gian để học về NEAR, sắp tới sẽ có nhiều nội dung khác trong series này!