
Cách biến ý tưởng của bạn thành một chương trình Solana (Hợp đồng thông minh)
Hãy cùng thiết kế một chương trình Solana. Hệ sinh thái Solana có nhiều bài viết giúp bạn hiểu mô hình lập trình Solana, cũng như xây dựng các chương trình demo nhỏ—một cách tuyệt vời để người mới học Solana và Anchor. Tuy nhiên, các chương trình thực tế đòi hỏi nhiều yếu tố phức tạp hơn.
Trong bài viết này, chúng ta sẽ xem xét một chương trình thực tế muốn xây dựng và tìm hiểu cách:
- Định nghĩa dữ liệu dưới dạng tài khoản Solana
- Tạo mối quan hệ giữa các dữ liệu
- Lưu trữ token bên trong chương trình
- Sử dụng chỉ mục để tăng tốc độ đọc
- Sử dụng phân mảnh để tăng tốc độ ghi
Mục tiêu của ví dụ này là giúp bạn hiểu cách mô hình hóa ý tưởng của riêng mình dưới dạng chương trình và tài khoản Solana.
Lưu ý: Bài viết này giả định rằng bạn đã nắm được kiến thức cơ bản về Anchor.
Ví dụ về chương trình Solana — Xây dựng thị trường dự đoán
Chương trình chúng ta thiết kế hôm nay là một thị trường dự đoán tương tự Polymarket, Hedgehog hoặc Drift BET. Nếu chưa quen với thị trường dự đoán, đây là nơi mọi người có thể đặt cược vào các kết quả khác nhau của một sự kiện. Đó có thể là đội nào thắng Super Bowl, ai giành giải Oscar ‘Đạo diễn xuất sắc nhất’, chính phủ có đưa ra thông báo trước một thời điểm cụ thể hay không, hoặc bất kỳ sự kiện thực tế nào khác có kết quả cụ thể. Nếu đặt cược vào kết quả chiến thắng, người chơi sẽ nhận được tiền thưởng từ quỹ cược.
Kiến trúc thị trường dự đoán
Dưới đây là kiến trúc cốt lõi của một thị trường dự đoán và mối quan hệ giữa từng thành phần:
- Có nhiều sự kiện.
- Mỗi sự kiện có nhiều kết quả. Cuối cùng, một trong các kết quả này sẽ được xác định là kết quả chiến thắng.
- Người dùng đặt nhiều cược vào từng kết quả. Khi người dùng đặt cược, tiền của họ được thêm vào quỹ tiền thắng cược của sự kiện đó.
Khi sự kiện được ‘phân định’ (tức là khi đã biết kết quả chiến thắng):
- Người dùng đặt cược vào kết quả chiến thắng có thể nhận tiền thưởng
- Người thắng sẽ nhận một phần quỹ tiền thắng cược (sau khi trừ phần của nhà cái)
- Phần quỹ tiền thắng cược của mỗi người thắng sẽ dựa trên tỷ trọng tiền họ đã đặt vào kết quả chiến thắng.
1. Thiết kế ứng dụng dưới dạng tài khoản Solana
Nếu thiết kế chương trình này để sử dụng cơ sở dữ liệu quan hệ, chúng ta sẽ cân nhắc:
- Các mục dữ liệu tương tự được lưu dưới dạng hàng trong một bảng
- Khóa chính được dùng để xác định duy nhất từng mục dữ liệu
- Các cột của bảng xác định thuộc tính dự kiến và kiểu dữ liệu của từng mục
Trong Solana, các khái niệm này gần tương ứng với:
- Các mục dữ liệu tương tự được lưu bằng cùng một loại tài khoản
- Địa chỉ được dùng để xác định duy nhất từng mục dữ liệu
- struct (các khóa và kiểu dữ liệu) xác định thuộc tính của từng mục
Dưới đây là cùng một dữ liệu, được biểu diễn cả trong bảng cơ sở dữ liệu truyền thống lẫn dưới dạng tài khoản Solana:
2. Ánh xạ mối quan hệ giữa các dữ liệu
Mối quan hệ giữa các mục dữ liệu hoạt động rất khác trong Solana. Cơ sở dữ liệu truyền thống sử dụng mối quan hệ (tức là kết nối logic giữa các bảng). Solana xử lý mối quan hệ một-nhiều dưới dạng vector địa chỉ, trong đó mỗi địa chỉ chứa một tài khoản có mục dữ liệu tương ứng.
Ví dụ: sự kiện có các 'kết quả' dưới dạng một vector gồm các địa chỉ kết quả. Anchor biểu diễn cấu trúc này là Vec<Pubkey>, mặc dù địa chỉ PDA (như kết quả của chúng ta) không thực sự là khóa công khai.
3. Lưu trữ token
Không giống cơ sở dữ liệu truyền thống, chương trình Solana còn có thể lưu trữ tiền—không chỉ các con số biểu thị số dư—bên trong một tài khoản.
Trong ví dụ này, sự kiện cần một tài khoản token cho quỹ tiền thắng cược. PDA của sự kiện sẽ sở hữu tài khoản quỹ tiền thắng cược. Khi người dùng đặt cược, họ gửi token vào tài khoản này. Quan trọng hơn, khi họ nhận tiền thắng cược, chương trình của chúng ta sẽ ký giao dịch với tư cách tài khoản sự kiện để chuyển token ra khỏi quỹ tiền thắng cược.
4. Giảm công sức đọc dữ liệu
Chương trình cần phản hồi người dùng nhanh nhất có thể, còn với tư cách nhà phát triển, chúng ta muốn tránh phải trả phí không cần thiết cho các lượt đọc tài khoản không cần thiết. Có thể giúp chương trình vừa phản hồi nhanh hơn vừa hiệu quả hơn bằng tổng lũy kế và chỉ mục.
Tính tổng lũy kế
Khi người dùng nhận tiền thắng cược, chúng ta cần biết chính xác tổng số tiền đã được đặt vào từng kết quả. Thị trường dự đoán xác định khoản thanh toán cho người thắng theo công thức quỹ tiền thắng cược × số tiền cược ÷ tổng số tiền cược vào kết quả chiến thắng.
Hiện tại, chúng ta chỉ lưu số tiền của từng lượt cược trong tài khoản cược tương ứng. Để tính tổng số tiền cược vào một kết quả cụ thể, chúng ta sẽ phải đọc mọi tài khoản cược rồi cộng các khoản tiền lại.
Thay vào đó, hãy thêm một trường vào mỗi kết quả—total_amount—và tăng giá trị đó khi người dùng đặt cược. Nhờ vậy, khi người dùng thắng, chúng ta có thể dễ dàng xác định khoản thanh toán mà không phải đọc mọi lượt cược vào kết quả đó.
Sử dụng chỉ mục
Chúng ta cũng cần tìm tất cả lượt cược từ một tài khoản người dùng cụ thể. Có thể tìm nạp từng tài khoản cược bằng getProgramAccounts() và lọc các tài khoản có trường người đặt cược được đặt thành địa chỉ của người dùng đó. Mặc dù getprogramAccounts() tốc độ cao của Helius giúp việc này nhanh hơn nhiều so với các nhà cung cấp RPC khác, chỉ mục vẫn là một giải pháp thay thế phổ biến.
Vì vậy, hãy tạo một chỉ mục để lưu các lượt cược của từng người dùng. Khi người dùng đặt cược mới, chúng ta sẽ tạo mục này nếu chưa tồn tại và thêm lượt cược vào danh sách cược của người dùng đó:
Chúng ta cũng sẽ thêm thẻ vào từng sự kiện để dễ dàng tìm tất cả sự kiện có thẻ 'thể thao', ‘chính trị’, 'châu Âu', 'Hoa Kỳ', 'chính trị', v.v. Chúng ta sẽ tạo thêm một chỉ mục cho mục đích đó:
Giờ đây, chúng ta có thể dễ dàng truy xuất tất cả sự kiện cần tìm bằng cách xem tài khoản thẻ sự kiện.
5. Giảm tranh chấp ghi
Bạn còn nhớ mỗi sự kiện có một tài khoản token duy nhất để lưu các khoản cược cho sự kiện đó không? Mỗi khi người dùng thêm một lượt cược mới, token sẽ được chuyển vào tài khoản này. Nói cách khác, tài khoản sẽ được ghi dữ liệu.
Solana nhanh nhờ thực hiện song song các thao tác. Tuy nhiên, không thể cập nhật số dư trên một tài khoản duy nhất theo cách song song—việc này phải diễn ra tuần tự vì tại mỗi thời điểm, mỗi tài khoản chỉ được có một số dư duy nhất.
Nếu một sự kiện mới được công bố và nhận nhiều lượt cược, nhiều thao tác ghi sẽ đồng thời diễn ra trên tài khoản token của sự kiện và giao dịch của chương trình có thể trở nên chậm. Hiện tượng này được gọi là tranh chấp ghi—nhiều giao dịch cùng cạnh tranh quyền truy cập vào tài khoản.
Một cách để cho phép xử lý song song trong những trường hợp này là sử dụng phân mảnh. Một tài nguyên được chia thành nhiều phần—gọi là các phân mảnh—có thể được truy cập song song.
Cách phân mảnh hoạt động
Các khoản thanh toán đến được gửi tới những phân mảnh win_pool riêng biệt dựa trên giá trị byte cuối cùng trong khóa công khai của người đặt cược, bằng cách sử dụng macro shard_num(). Điều này đảm bảo các khoản thanh toán đến được xử lý nhanh chóng.
Sau đó, một trình xử lý lệnh dành cho quản trị viên có thể hợp nhất các phân mảnh này vào một tài khoản quỹ tiền thắng cược duy nhất, đảm bảo chúng ta có thanh khoản trong cùng một tài khoản để trả thưởng cho người thắng.
Chúng ta cũng cần xem xét tranh chấp ghi nếu một sự kiện phổ biến được phân định và tất cả người thắng cùng lúc nhận tiền thưởng. Chúng ta cũng cần chuyển tiền khỏi quỹ tiền thắng cược nhanh nhất có thể.
Lựa chọn tốt nhất ở đây là tránh quy trình yêu cầu nhận thưởng. Thay vào đó, nếu gửi tiền tuần tự ngay sau khi sự kiện được phân định, người dùng sẽ hoàn toàn không phải xử lý các yêu cầu nhận thưởng chậm—tiền thắng cược đã được gửi vào tài khoản của họ.
Tuy nhiên, bạn cần thực sự có người dùng trước khi phải lo về đám đông cùng đặt cược hoặc nhận tiền thắng cược. Nếu đang lên kế hoạch cho chương trình Solana nhưng chưa ra mắt, bạn không cần thực hiện các biện pháp tối ưu hóa để xử lý lượng lớn lưu lượng người dùng mà mình chưa có. Nếu không cần các biện pháp tối ưu hóa lớn như vậy để ra mắt, bạn nên cân nhắc mức độ phức tạp phát sinh, đồng thời lưu ý rằng có thể phải triển khai chúng khi chương trình trở nên phổ biến hơn.
Kết luận
Trong bài viết này, chúng ta đã tìm hiểu chuyên sâu về cách tạo một ứng dụng thực tế trên Solana. Giờ đây, bạn đã có kiến thức thực tiễn về cách định nghĩa tài khoản Solana, thiết lập mối quan hệ dữ liệu, lưu trữ token và tối ưu hóa hiệu suất bằng chỉ mục cũng như phân mảnh.
Nếu có thêm câu hỏi, đừng ngần ngại liên hệ với @helius trên X hoặc tham gia Discord của Helius. Trong tương lai, chúng tôi sẽ phát triển thêm ví dụ về thị trường dự đoán này, vì vậy hãy nhớ theo dõi các tài khoản đó để nhận thông tin cập nhật.
Với những kỹ năng mới này, đã đến lúc biến ý tưởng của bạn thành các chương trình Solana và hiện thực hóa chúng trong hệ sinh thái Solana. Chúc bạn lập trình vui vẻ! Cảm ơn Ichigo đã đánh giá bài viết này và r0bre đã chỉ ra kỹ thuật phân mảnh tài khoản được sử dụng.
Tài nguyên bổ sung
Bài viết liên quan
Đăng ký nhận tin từ Helius
Luôn cập nhật những thông tin mới nhất về phát triển Solana và nhận thông báo khi chúng tôi đăng bài


