Transaction & Script

Transaction và Scripts đều cần thiết cho bất kỳ ứng dụng blockchain nào. Nếu không có chúng, chúng ta sẽ không thể tương tác với blockchain. Trên Flow, chúng thậm chí còn đặc biệt hơn vì cả hai đều tách biệt khỏi hợp đồng. Nếu bạn đã viết mã trên Ethereum trước đây, bạn sẽ biết rằng các transaction chỉ là các chức năng bạn gọi bên trong hợp đồng (nếu bạn không biết điều đó, không sao cả!). Tuy nhiên, trên Flow, các transactions và scripts hoạt động như một loại “người trung gian” giữa người tương tác với blockchain và hợp đồng thông minh. Nó trông giống như thế này:

Flow Transaction & Scripts
Transaction & Scripts

Bây giờ, sự khác biệt giữa transactions và scripts là gì? Sự khác biệt lớn nhất là các transaction sửa đổi dữ liệu trên blockchain và các script xem dữ liệu trên blockchain. Đây là một sơ đồ hữu ích để hiểu sự khác biệt:

Transaction & Script
Transactions & Scripts

Scripts

Cùng xem lại ví dụ trong series #2

Chạy trên playground, sao chép hợp đồng này vào tài khoản 0x01 và nhấp vào “Deploy”:

pub contract HelloWorld {

    pub let loichao: String

    init() {
        self.loichao = "Hello, World!"
    }
}

Nhấp vào tab Script ở phía tay trái:

import HelloWorld from 0x01

pub fun main(): String {
    return HelloWorld.loichao
}

Nếu bạn nhấp vào “Execute,”, bạn sẽ thấy “Hello, World!” trong console. Tuyệt vời! Những gì bạn vừa làm là chạy một tập lệnh. Lưu ý rằng không cần thanh toán và chúng tôi đã xem dữ liệu trong hợp đồng thông minh của mình.

Transactions

Bây giờ, hãy làm một ví dụ về transaction. Ở phía bên trái, bên dưới “Transaction Templates”, hãy nhấp vào tab “Transaction”. Hãy tiếp tục và xóa mọi thứ trong tab đó để nó trông như thế này:

Transaction tab
Transaction tab

Bây giờ, chúng ta muốn sửa đổi dữ liệu trên blockchain. Để làm được điều đó, hãy thiết lập giao dịch. Chúng ta có thể làm điều đó bằng cách đặt câu lệnh này:

transaction() {
    prepare(signer: AuthAccount) {}

    execute {}
}

Đây là một giao dịch trống không làm bất cứ điều gì. Để giải thích những gì prepareexecute, chúng ta cần tạm dừng và nói về các Accounts trên Flow.

Accounts (tài khoản) trên Flow

Trên Flow, các tài khoản có thể lưu trữ dữ liệu của riêng họ. Điều đó có nghĩa là gì? Nếu bạn sở hữu một NFT (NonFungibleToken) trên Flow, thì NFT đó sẽ được lưu trữ trong tài khoản của bạn. Điều này rất khác so với các blockchain khác như Ethereum. Trên Ethereum, NFT của bạn được lưu trữ trong hợp đồng thông minh. Trên Flow thực sự cho phép các tài khoản tự lưu trữ dữ liệu của riêng họ, điều này thật tuyệt vời. Nhưng làm cách nào để chúng ta truy cập dữ liệu trong tài khoản của họ?

Chúng ta có thể làm điều đó với loại AuthAccount. Mỗi khi người dùng (như bạn và tôi) gửi một giao dịch, bạn phải trả tiền cho giao dịch và sau đó bạn “ký” vào giao dịch đó. Tất cả điều đó có nghĩa là bạn đã nhấp vào nội dung “Tôi muốn phê duyệt giao dịch này.” Khi bạn ký tên, giao dịch sẽ lấy AuthAccount của bạn và có thể truy cập dữ liệu trong tài khoản của bạn.

Bạn có thể thấy điều này được thực hiện trong phần prepare của transaction và đó là toàn bộ giai đoạn chuẩn bị: truy cập thông tin/dữ liệu trong tài khoản. Mặt khác, execute không thể làm điều đó. Nhưng nó có thể gọi các chức năng và thực hiện các công việc để thay đổi dữ liệu trên blockchain.

LƯU Ý: Trên thực tế, bạn không bao giờ thực sự cần giai đoạn execute. Về mặt kỹ thuật, bạn có thể làm mọi thứ trong giai đoạn chuẩn bị, nhưng mã không rõ ràng theo cách đó. Tốt hơn là tách logic.

Quay lại ví dụ

Vì vậy chúng ta muốn thay đổi trường lời chào của mình thành một thứ khác ngoài “Hello, World!” Chúng ta cần phải thêm các chức năng trong hợp đồng thông minh để làm điều đó.
Quay lại tài khoản 0x01 và thêm chức năng này vào bên trong hợp đồng:

pub fun changeGreeting(loichaomoi: String) {
    self.loichao = loichaomoi
}

Điều đó có nghĩa là gì? Hãy nhớ những ngày trước chúng ta đã học gì về hàm. Bạn thiết lập chúng như sau: [access modifier] fun [tên hàm](parameter1: Type, parameter2: Type, ...): [return type] {}

Để giữ cho mọi thứ đơn giản, chúng ta đang sử dụng pub làm access modifier. pub có nghĩa là chúng ta có thể gọi hàm này từ bất kỳ đâu (trong hợp đồng hoặc trong giao dịch). Chúng ta cũng nhận một tham số loichaomoi là một String và đặt lời chào của mình bằng với lời chào mới.

Nhưng đợi đã! Có lỗi trong hợp đồng. “cannot assign to constant member: loichao” Tại sao lại vậy? Hãy nhớ rằng, chúng tôi đã thực hiện lời chào của mình là let. let có nghĩa là nó là một hằng số (không thể thay đổi), nên muốn thay đổi lời chào ta phải đổi thành var. Đảm bảo nhấn “Deploy” lần nữa. Mã của bạn bây giờ sẽ trông như thế này:

pub contract HelloWorld {

    pub var loichao: String

    pub fun changeGreeting(loichaomoi: String) {
        self.loichao = loichaomoi
    }

    init() {
        self.loichao = "Hello, World!"
    }
}

Bây giờ chúng ta đã thiết lập lại hợp đồng của mình, hãy quay lại transaction. Trước tiên, hãy đảm bảo nhập hợp đồng HelloWorld như sau: import HelloWorld from 0x01. Sau đó, chúng ta phải quyết định: chúng ta muốn gọi changeGreeting ở đâu? Trong giai đoạn prepare, hay giai đoạn execute? Câu trả lời là giai đoạn execute vì chúng ta không truy cập bất kỳ dữ liệu nào trong tài khoản, chỉ thay đổi một số dữ liệu trong hợp đồng thông minh.

Chúng ta có thể làm điều đó bằng cách thêm dòng này vào giai đoạn execute: HelloWorld.changeGreeting(loichaomoi: myNewGreeting). Khi bạn gọi một hàm trong Cadence, bạn chuyển các tham số bằng cách thực hiện (argumentLabel: value), trong đó ArgumentLabel là tên của đối số và value là giá trị thực. Bạn sẽ nhận thấy rằng chúng ta gặp lỗi myNewGreeting không được xác định, điều này hợp lý vì chúng ta không nhận được lỗi đó từ bất kỳ đâu. Vì vậy, hãy thêm một tham số có tên myNewGreeting vào giao dịch để chúng ta có thể chuyển vào một giá trị cho lời chào mới. Chúng ta có thể làm điều đó như vậy:

import HelloWorld from 0x01

transaction(myNewGreeting: String) {

  prepare(signer: AuthAccount) {}

  execute {
    HelloWorld.changeGreeting(loichaomoi: myNewGreeting)
  }
}
newGreeting
newGreeting

Lưu ý rằng: chúng ta có thể “ký” giao dịch này từ bất kỳ tài khoản nào. Vì nó không thực sự quan trọng (chúng ta không truy cập dữ liệu trong tài khoản), vui lòng chọn bất kỳ tài khoản nào bạn muốn.

Sau khi bạn nhấp vào “Send”, hãy quay lại Script của bạn và nhấp vào “Execute”. Bây giờ bạn sẽ thấy “Tôi là Hải” được in trong bảng điều khiển Console. Bạn vừa thực hiện thành công giao dịch đầu tiên của mình.

Tôi là Hải
Tôi là Hải

Ví dụ khác về Transaction & Script để thay đổi số

Trong Accounts 0x02, chúng ta sẽ tạo một hợp đồng thông minh tên là Mynumber

  • Đặt biến số (variable) tên là number dưới dạng Int(Nghĩa là Interger, là số), đặt là 0 ban đầu.
  • Tạo Function tên là changenumbersẽ nhận số mới tên là newnumberlà tham số dưới dạng Intvà thay đổi number thành newnumber.
  • Thêm script tên để đọc number trong hợp đồng.
  • Tạo transaction để lấy tham số tên là myNewNumber và chuyển tham số đó vào hàm changeNumber. Xác nhận lại sự thay đổi số trên script lại.

Câu lệnh sẽ như sau:

Hợp đồng thông minh

pub contract Mynumber {
   pub var number: Int  
   pub fun changenumber(newnumber: Int) {
   self.number = newnumber
   }

   init() {
     self.number = 0
   }
}

Script

import Mynumber from 0x02
   pub fun main(): Int {
   return Mynumber.number
}

Transaction

import Mynumber from 0x02

    transaction(myNewNumber: Int) {
    prepare(signer: AuthAccount) {}
       
    execute {
       Mynumber.changenumber(newnumber: myNewNumber)
    }
}

Kết quả khởi chạy ban đầu

Kết quả 1
Kết quả 1

Kết quả sau khi gọi transaction ở Accounts 0x05 với giá trị là 100

newnumber
newnumber

Cộng đồng hệ sinh thái Flow Việt Nam