Week 06 - Oracles
Ghi chú
Đây là phiên bản viết của Bài giảng số 6.
Trong bài giảng này, chúng ta học về oracles và sử dụng PAB (Plutus Application Backend).
Những ghi chú này sử dụng Plutus cam kết: 476409eaee94141e2fe076a7821fc2fcdec5dfcb
#
Tổng quatTrong bài giảng này, chúng ta sẽ xem xét một nghiên cứu điển hình, để xem làm thế nào chúng ta đã học được cho đến nay có thể được biến thành một ứng dụng thực tế. Một bộ sưu tập các tệp thực thi thậm chí đi kèm với một giao diện người dùng nhỏ.
Nó sẽ là một dApp thực sự, ngoài việc chúng ta chưa có sẵn một blockchain thực sự. Điều này sẽ chạy trên một chuỗi khối mô phỏng - một chuỗi mô hình.
Ví dụ chúng ta sẽ sử dụng cho việc này là triển khai một Oracles rất đơn giản.
Có rất nhiều ví dụ về các trường hợp sử dụng cho oracles. Chúng ta có thể nghĩ đến các nguồn dữ liệu bên ngoài như dữ liệu thời tiết, kết quả bầu cử, dữ liệu trao đổi chứng khoán hoặc sự ngẫu nhiên. Ví dụ, bạn có thể có một hợp đồng cá cược phụ thuộc vào kết quả của một trò chơi thể thao cụ thể.
Có nhiều cách khác nhau để thực hiện các câu chuyện thần thoại, với mức độ phức tạp khác nhau.
Chúng tôi sẽ sử dụng một cách tiếp cận rất đơn giản, nơi chúng tôi có một nhà cung cấp dữ liệu đáng tin cậy. Và để làm ví dụ về dữ liệu, chúng tôi sẽ sử dụng tỷ giá hối đoái ADA/USD.
Có rất nhiều vấn đề với cách tiếp cận này, vì chúng ta phải tin tưởng vào nguồn dữ liệu. Có nhiều cách để giảm thiểu rủi ro nguồn dữ liệu không đáng tin cậy. Ví dụ: chúng tôi có thể yêu cầu nhà cung cấp đưa ra một số tài sản thế chấp bị mất nếu dữ liệu không được cung cấp hoặc cung cấp không chính xác. Bạn có thể kết hợp nhiều Oracles thành một và chỉ chấp nhận kết quả nếu tất cả chúng đều giống nhau hoặc giá trị trung bình của các nguồn khác nhau. Bạn cũng có thể nghĩ ra các cơ chế phức tạp hơn.
Như chúng ta đã biết, đối với bất kỳ điều gì xảy ra trên blockchain, phải có UTxO, vì vậy điều hiển nhiên cần làm là biểu thị nguồn cấp dữ liệu dưới dạng UTxO. UTxO nằm ở địa chỉ tập lệnh của oracle và trường Datum của nó mang giá trị hiện tại của dữ liệu oracle.
Và đây là nơi chúng tôi tìm ra vấn đề đầu tiên của mình. Như chúng ta đã lưu ý trước đây, xác thực chỉ xảy ra khi bạn muốn sử dụng thứ gì đó từ địa chỉ tập lệnh, chứ không phải khi bạn tạo đầu ra tại địa chỉ tập lệnh. Điều này có nghĩa là chúng tôi không thể ngăn cản bất kỳ ai tạo ra kết quả đầu ra tùy ý tại địa chỉ tập lệnh.
Bằng cách nào đó, chúng ta cần phân biệt đầu ra oracle thực sự với các đầu ra khác có thể ở cùng một địa chỉ tập lệnh. Và cách chúng tôi làm điều này là đặt một NFT ở đầu ra. Vì một NFT chỉ có thể tồn tại một lần nên chỉ có thể có một UTxO tại địa chỉ tập lệnh chứa NFT.
Làm thế nào một Oracles như vậy có thể được sử dụng?
Ở đây chúng ta đến với một cái gì đó mà chúng ta chưa từng thấy trước đây. Trong tất cả các hợp đồng và trình xác thực viết mã của chúng tôi, chúng tôi luôn biết trước toàn bộ API. Trong trường hợp của một nhà Oracles, điều này là khác nhau. Tại thời điểm mà một Oracles được tạo ra, bạn không biết mọi người có thể muốn sử dụng nó như thế nào. Nó phải giống như một API mở, có thể hoạt động với các hợp đồng thông minh chưa được thiết kế.
Ví dụ về trường hợp sử dụng có thể sử dụng oracle cụ thể này, chúng ta hãy xem xét một hợp đồng hoán đổi trong đó, tại địa chỉ hoán đổi, ai đó có thể gửi ADA và sau đó người khác có thể lấy những ADA đó để đổi lấy USD.
Tất nhiên, chúng ta không có USD trực tiếp trên blockchain, nhưng chúng ta có thể tưởng tượng rằng chúng được đại diện bởi một số token gốc.
Trong ví dụ này, vì giá trị của oracle là 1,75, thì nếu ai đó cung cấp 100 ADA, giá trị cho điều đó sẽ là 175 USD.
Ngoài ra, chúng tôi cần một động lực để Oracles cung cấp dữ liệu, bởi vì ngoài các chi phí khác để cung cấp dữ liệu, thì ở mức tối thiểu họ sẽ phải trả phí để tạo UTxO.
Vì vậy, giả sử rằng nhà cung cấp oracle xác định một khoản phí 1 ADA phải trả mỗi khi oracle được sử dụng.
Trong ví dụ này, điều đó có nghĩa là người muốn ADA sẽ phải trả 175 USD cho người bán ADA và 1 ADA cho nhà Oracles.
Giao dịch sẽ như thế nào?
Trước hết, logic xác thực hoán đổi sẽ cần quyền truy cập vào giá trị oracle hiện tại, có nghĩa là UTxO oracle phải là đầu vào cho giao dịch.
Sau đó, chúng ta có logic xác thực Oracles. Trong trường hợp này, chúng tôi muốn sử dụng Oracles. Vì vậy, giả sử chúng ta có một Redeemer được gọi là use
. Bây giờ, trình xác thực Oracles phải kiểm tra một số thứ.
- NFT có trong đầu vào được tiêu thụ không?
- Có đầu ra từ giao dịch tại cùng một địa chỉ có chứa cùng một NFT không?
- Giá trị trong UTxO đầu ra có giống với giá trị đầu vào không?
- Phí có không?
Bây giờ chúng ta có thể hoàn thành giao dịch.
Chúng tôi sử dụng hai đầu vào bổ sung - phí do người mua trả và 100 ADA do người bán đặt cọc. Sau đó, chúng tôi có hai đầu ra bổ sung - 175 USD cho người bán và 100 ADA cho người mua. Và đối với những đầu vào và đầu ra mới này, người xác nhận hoán đổi có trách nhiệm đảm bảo rằng nó chính xác. Trong khi đó, trình xác nhận Oracles chỉ quan tâm đến việc đảm bảo rằng mọi thứ liên quan đến Oracles là chính xác.
Chỉ cần nhấn mạnh, hợp đồng hoán đổi này chỉ là một ví dụ. Nhà Oracles phải có khả năng làm việc với nhiều hợp đồng thông minh khác nhau muốn sử dụng dữ liệu của nó.
Nếu đây là tất cả, thì chúng ta sẽ không cần một Oracles. Nếu giá trị đã được cố định, để nó luôn là 1,75 thì chúng tôi có thể chỉ cần mã hóa giá trị này vào hợp đồng của mình. Vì vậy, giá trị phải có thể thay đổi. Ít nhất, trong một ví dụ chẳng hạn như ví dụ này, nơi chúng ta có một tỷ giá hối đoái, tất nhiên, có thể thay đổi theo thời gian. Có thể có các ví dụ khác như kết quả của một trận đấu thể thao, trong đó nó là một sự kiện kỳ lạ trong lịch sử, nhưng trong trường hợp này, điều quan trọng là nó có thể thay đổi.
Điều này có nghĩa là trình xác thực oracle, ngoài việc sử dụng Redeemer, phải có khả năng hỗ trợ một hoạt động khác mà nhà cung cấp oracle thực sự có thể thay đổi dữ liệu.
Vì vậy, giả sử giá trị thay đổi từ 1,75 thành 1,77.
Chúng tôi biết rằng trên blockchain (E)UTxO, không có gì thay đổi, vì vậy bạn không thể thay đổi dữ liệu của UTxO hiện có. Tất cả những gì bạn có thể làm là sử dụng UTxO và sản xuất UTxO mới.
Chúng tôi sẽ có một giao dịch sử dụng bản cập nhật Redeemer. Logic xác nhận hơi khác. Nó giống như trước đây ở chỗ NFT cần phải có mặt trong đầu vào oracle đã tiêu thụ, và cũng cần có mặt trong đầu ra mới. Ngoài ra, giao dịch phải được ký bởi nhà cung cấp oracle. Và, chúng tôi có thể sử dụng giao dịch cập nhật này như một cơ hội để nhà cung cấp oracle thu phí.
Chúng tôi nhấn mạnh rằng NFT có trong đầu ra, nhưng chúng tôi không nói gì về các giá trị khác. Tất cả các khoản phí do các giao dịch khác sử dụng dữ liệu oracle này có thể được thu trong quá trình cập nhật giao dịch.
#
Bản tóm tắtTóm lại, chúng tôi đại diện cho Oracles bởi một UTxO và xác định UTxO chính xác bằng một NFT. Giá trị oracle là dữ liệu của UTxO. Chúng tôi hỗ trợ hai hoạt động.
Một là sử dụng sử dụng Oracles trong một số giao dịch tùy ý. Các validator use
sẽ đảm bảo rằng các đầu vào oracle tiêu thụ mang NFT, rằng có một đầu ra mà lại mang NFT, không làm thay đổi dữ kiện, và cộng phí.
Thao tác thứ hai là cập nhật chỉ có thể được thực hiện bởi nhà cung cấp oracle. Đối với một giao dịch cập nhật, đầu vào oracle lại phải mang NFT, phải có đầu ra oracle cũng mang NFT. Không có hạn chế nào nữa. Số liệu có thể thay đổi và phí tích lũy có thể được lấy ra.
#
Oracle CoreBây giờ chúng ta biết nó hoạt động như thế nào, hãy xem một số đoạn mã.
#
On-chainĐầu tiên, hãy xem mã Plutus tự thực thi oracle.
Oracle sẽ là một hợp đồng được tham số hóa và nó sẽ phụ thuộc vào bốn trường.
oSymbol
là biểu tượng tiền tệ của NFT được sử dụng để xác định giao dịch. Chúng tôi không cần tên token vì chúng tôi sẽ chỉ sử dụng chuỗi trống làm tên token.oOperator
là chủ sở hữu của oracle - hàm băm của chủ sở hữu khóa công khai có thể thực hiện cập nhậtoFee
là khoản phí lovelace phải trả mỗi khi sử dụng oracleoAsset
đại diện cho loại tài sản mà chúng tôi muốn trao đổi tỷ giá so với Ada, trong trường hợp của chúng tôi sẽ là một số loại token USD
Redeemer sẽ hỗ trợ hai hoạt động.
Chúng ta cần xác định lớp tài sản NFT. Như đã đề cập, chúng ta sẽ sử dụng chuỗi trống cho tên token.
Các oracleAsset
sẽ được sử dụng để xác định các NFT - đây không phải là oAsset
định nghĩa ở trên.
Chúng tôi tạo một hàm trợ giúp nhỏ gọi là oracleValue
. Điều này nhận một giao dịch đầu ra và một hàm tìm kiếm dữ liệu, sau đó trả về integer
. Các Integer
đại diện cho tỷ giá hối đoái (ví dụ 1,75) nhân với một triệu. Điều này tránh những phức tạp có thể xảy ra khi sử dụng số thực.
Hàm này là một ví dụ về tính toán monadic trong monad. nó không phải là IO
hoặc Contract monad
. Đầu tiên chung ta gọi txOutDatum
, Nó không thành công nếu đâu ra không có datum
. Nếu nó thành công, chúng tôi nhận được một băm datum
mà chúng tôi có thể tham chiếu trong đó dh
. Tiếp theo, chúng tôi sử dụng hàm f
được cung cấp làm đối số thứ hai để có thể biến băm dữ liệu này thành một dữ liệu. Điều này cũng có thể thất bại. Nếu nó thành công, chúng tôi có thể tham khảo kết quả trong d
. Datum
chỉ là một trình bao bọc kiểu mới xung quanh Data
,vì vậy sau đó chúng ta có thể sử dụng PlutusTx.fromData
để có thể biến d
thành Integer
. Một lần nữa, điều này có thể không thành công, bởi vì ngay cả khi dữ liệu ở đó, nó có thể không được chuyển đổi thành giá trị số nguyên.
Chúng ta sẽ thấy chúng ta sử dụng hàm oracleValue
ở đây trong giây lát.
Chức năng quan trọng nhất là mkOracleValidator
.
Hàm mkOracleValidator
nhận 4 tham số: Oracle
,
trong ví dụ này datum
là Integer
, redeemer
là
OracleRedeemer
và cuối cùng là ScriptContext
.
Có hai trường hợp cho trình xác thực này - use
và update
- nhưng có những điểm tương đồng. Trong cả hai trường hợp, chúng tôi muốn kiểm tra xem chúng tôi có đầu vào chứa NFT và có đầu ra chứa NFT không.
Vì cả hai việc kiểm tra này đều cần được thực hiện bất kể trường hợp sử dụng nào, nên chúng được thực hiện trước.
Sau đó, chúng tôi xem xét trường hợp sử dụng mà chúng tôi đang xử lý.
Trước khi xem xét hàm inputHasToken
, có một chức năng trợ giúp khác cần xem xét
Hàm ownInput
trả về TxOut
Mà nó đang có gắng tiêu thụ, trong trường hợp này là oracle output. Trường hợp Nothing
có thể sẩy ra nếu ra nếu chúng ta đang ở trong một context khác, chẳng hạn như context đúc, vì vậy tình huống này sẽ không xảy ra cho chúng ta. Hàm findOwnInput
được cung cấp bởi Plutus và sẽ nhận context, sau đó tìm các đầu vào liên quan. Hàm txInInfoResolved
nhận TxOut
từ TxInInfo
.
Hàm inputHashToken
kiểm tra token hiện tại. Nó sử dụng hàm assetClassValueOf
để tìm kiếm NFT trong phản hồi ownInput
.
Hàm trợ giúp tiếp theo, ownOutput
kiểm tra xem chúng có chính xác một đầu ra hay không và trả lại đầu ra đó cho chúng.
Chúng ta có thể sử dụng hàm trợ giúp outputHasToken
giống như hàm inputHashToken
.
Điều đó bao gồm mã cho các trường hợp phổ biến. Bây giờ, hãy xem mã cụ thể cho trường hợp update
.
Có hai điều kiện để kiểm tra. Đầu tiên là nhà điều hành đã thực sự ký kết giao dịch. Điều này rất đơn giản nên chúng ta có thể thực hiện nó on-chain mà không cần hàm trợ giúp.
Điều tiếp theo cần kiểm tra là dữ liệu đầu ra. Chúng tôi biết rằng giá trị có thể thay đổi, nhưng chúng tôi cần kiểm tra xem ít nhất nó có phải là kiểu đúng hay không.
Và đối với điều này, chúng tôi đã tham chiếu đến một hàm trợ giúp mới validOutputDatum
, chính nó sử dụng một hàm trợ giúp outputDatum
.
Điều này hoạt động bằng cách cố gắng lấy giá trị dữ liệu từ băm dữ liệu và sau đó cố gắng tạo giá trị oracle từ nó. Nếu thành công, nó sẽ trả về Just Integer
, ngược lại nó sẽ trả về Nothing
, vì vậy hàm validOutputDatum
chỉ cần kiểm tra xem giá trị trả về có phải Nothing
,hay là Just
.
Lưu ý rằng chúng tôi không kiểm tra bất kỳ điều gì về giá trị của Integer
. Giá trị này thậm chí có thể giữ nguyên như giá trị đầu vào, nếu giao dịch được sử dụng chỉ để thu phí đã tích lũy từ việc sử dụng oracle.
Trường hợp thứ hai cho mkOracleValidator
là trường hợp use
. Trường hợp này ai cũng có thể sử dụng được nhưng hạn chế hơn rất nhiều.
Đầu tiên, chúng tôi không cho phép giá trị thay đổi. Vì vậy, đây là điều kiện đầu tiên.
Chúng tôi đã viết hàm trợ giúp outputDatum
. Thay vì chỉ kiểm tra xem nó có phải là Integer
hay không , ở đây chúng ta cũng kiểm tra xem giá trị đầu ra của nó có giống với giá trị đầu vào hay không.
Và cuối cùng, chúng ta phải kiểm tra xem các khoản phí đã được thanh toán hay chưa. Và đối với điều này, chúng tôi sử dụng một chức năng trợ giúp mới được gọi là feesPaid
.
Hàm feesPaid
kiểm tra giá trị đầu ra ít nhất bằng giá trị đầu vào cộng với phí bắt buộc. Chúng tôi lại sử dụng toán tử <>
để cộng thêm fee vào giá trị đầu vào. Chúng tôi có thể đã sử dụng bằng (eq) thay vì lớn hơn hoặc bằng (geq). Việc sử dụng geq
ho phép người dùng oracle đưa cho nhà cung cấp oracle một mẹo, nếu họ muốn dùng.
Vì vậy, đây về cơ bản là logic cốt lõi của oracle như được thể hiện trong sơ đồ sau.
Bây giờ chúng ta có boilerplate của chúng ta. Đặc biệt lưu ý rằng chúng tôi sử dụng mẫu mà chúng tôi cần cho trình xác thực được tham số hóa.
Và điều này kết thúc phần on-chain của mã oracle.
#
Off-chainChúng tôi cũng tạo một số mã off-chain, cụ thể là để bắt đầu Oracles và cập nhật nó. Tuy nhiên, chúng tôi không viết mã off-chain cho use
của oracle. Đó không phải là trách nhiệm của hợp đồng này. Đó sẽ là trách nhiệm của người muốn sử dụng oracle - họ sẽ viết mã để tạo giao dịch với use của redeemer. Đây là lần đầu tiên chúng tôi thấy trường hợp chúng tôi có một số mã trong chuỗi không được ghép nối với một số mã off-chain.
#
Bắt đầu với OracleĐể bắt đầu oracle, chúng ta cần một số tham số.
opFees
tham số đại diện cho số phí lovelace đó sẽ được tính vào sử dụng oracle.
Tham số opSymbol
và opToken
đại diện cho token
mà chúng tôi đang cung cấp tỷ giá hối đoái Ada, trong trường hợp này là token USD.
Đầu tiên, chúng tôi tạo một hàm startOracle
, có trách nhiệm tạo ra NFT sẽ được sử dụng để xác định UTxO oracle. Các hàmstartOracle
này sẽ không cung cấp một giá trị ban đầu cho oracle, điều này sẽ được xử lý bởi các hàm updateOracle
. Lý do cho điều này là, nếu chúng tôi cung cấp một giá trị ban đầu, nó có thể bị lỗi thời vào thời điểm NFT được đúc.
Chúng tôi có thể đã sử dụng cùng một mã để đúc NFT như chúng tôi đã sử dụng trong bài giảng 5. Điều này sẽ hoạt động hoàn toàn tốt.
Tuy nhiên, đây là một mô-đun tiền tệ được cung cấp trong plutus-use-cases
cái mà cung cấp hàm forgeContract
cho phép tạo NFT
Đây là hàm forgeContract
hiện thị trong REPL.
Phần quan trọng bắt đầu về phía cuối, nơi mà tham số đầu tiên - của kiểu PubKeyHash
- được định nghĩa. Đây là băm của khóa công khai của người nhận NFT.
Hàm forgeContract
cung cấp chức năng tổng quát hơn hợp đồng NFT trước của chúng tôi. Nó cho phép tạo nhiều NFT trong một lần. Nó sẽ tạo ra một ký hiệu tiền tệ chỉ có thể được sử dụng một, tương tự như NFT của chúng tôi từ lần trước, vì vậy chỉ có thể có một giao dịch đúc tiền. Nhưng đối với một biểu tượng tiền tệ, bạn có thể đúc nhiều token khác nhau trong cùng một giao dịch, với nhiều tên token khác nhau và với số lượng khác nhau. Tham số thứ hai cho phép chúng tôi xác định các tên và số lượng token này.
Và nó cho chúng ta một Contract
nó trả về một gia trị kiểu OneShotCurrency
. Loại này dành riêng cho tiền tệ và nó không thực sự quan trọng đối với chúng tôi. Tất cả những gì quan trọng đối với chúng tôi là chúng tôi có thể lấy lại biểu tượng tiền tệ từ nó.
Có một vấn đề nhỏ. Điều này không tương thích với những gì chúng tôi muốn. Chúng tôi muốn các loại này
Một kiểu người viết tùy ý (vì chúng ta không sử dụng nó), một lược đồ tùy ý (miễn là chúng ta có sẵn BlockChainActions
), Text
thông báo lỗi và kiểu trả về Oracle
.
Vấn đề là giá trị Contract
trả về forgeContract
không cho phép Text
thông báo lỗi. Bạn có thể thấy điều này trong đầu ra chi tiết từ REPL - có một ràng buộc đối với tham số e
.
Thật không may là Text
không thực hiện AsCurrencyError
.
May mắn thay, có một hàm có thể trợ giúp
Với một Contract
, nó cho phép chúng tôi tạo mới một Contract
với một loại thông báo lỗi mới.Điều đó được cung cấp, chúng tôi cung cấp một hàm chuyển đổi từ loại lỗi đầu tiên sang loại lỗi thứ hai.
Vì vậy, chúng ta hãy nhìn vào hàm startOracle
.
Ở đây chúng ta thấy hàm chuyển đổi lỗi được cung cấp dưới dạng pack . show
.
Hàm show
chuyển đổi lỗi cho một String
và hàm pack
chuyển đổ một String
tới một kiểu Data.Text
.
Tại thời điểm này osc
giữ OneShotCurrency
, Và chúng ta có thể sử dụng hàm currencySymbol
để lấy ký hiệu tiền tệ như cs
.
Hàm currencySymbol
có kiểu:
và được sử dụng phù hợp
Bây giờ chúng tôi đã đúc NFT của mình và nó có ký hiệu tiền tệ cs
. và bây giờ chúng ta có thể xây dựng giá trị tham số Oracle
.
lý do là opSymbol
và opToken
được định nghĩa riêng trong kiểu OracleParams
. op
chỉ là điều này làm cho việc này trở nên dễ dàng hơn khi chúng ta sử dụng playground.
#
Cập nhật OracleHàm updateOracle
là phức tạp hơn.
Chức năng này phải xử lý hai trường hợp. Cụ thể là trường hợp chúng tôi có một giá trị mà chúng tôi muốn cập nhật và trường hợp 2 chúng tôi mới bắt đầu tạo oracle và chúng tôi muốn tạo một giá trị lần đầu tiên.
Nó lấy các tham số oracle and Integer
giá trị mà chúng tôi muốn có oracle giữ.
Đầu tiên, chúng tôi tạo một chức năng trợ giúp findOracle
.
Mục đích của findOracle
là để tra cứu UTxO oracle hiện có. Điều này có thể không thành công vì có thể không có Oracles ở đó. Điều này sẽ xảy ra nếu chúng ta mới bắt đầu oracles và chưa tạo UTxO với giá trị oracle. Tuy nhiên, nếu chúng tôi tìm thấy nó, chúng tôi trả về một bộ ba chứa mã nhận dạng UTxO (TxOutRef
), chính UTxO
, chứa tất cả dữ liệu (TxOutTx
) và giá trị oracle (tỷ giá hối đoái hiện tại do oracle nắm giữ). Các Integer
chứa giá trị oracle được mã hóa cũng trong giá trị TxOutTx, nhưng chúng tôi thêm nó vào ba để làm cho nó dễ dàng hơn để làm việc với.
Điều đầu tiên chúng tôi làm là lấy tất cả các UTxO ở địa chỉ này. Nhưng chỉ một trong số này sẽ là thứ mà chúng tôi đang tìm kiếm - cái có chứa NFT.
Chúng tôi thực hiện điều này bằng cách sử dụng hàm Map.filter
nhận một hàm làm tham số, trong trường hợp này, trả về True
cho UTxO nơi NFT hiện diện.
Chúng ta sẽ kết thúc với một map utxos
trống hoặc chứa một mục. Bây giờ, chúng ta phân biệt giữa hai trường hợp này.
Chúng tôi chuyển đổi map
thành danh sách list
các bộ giá trị đại diện cho các cặp giá trị quan trọng của id giao dịch và chính các giao dịch.
Đối với trường hợp không có phần tử, chúng tôi sử dụng trường hợp _ để đại diện cho tất cả các trường hợp khác. Đây chỉ có thể là danh sách trống, nhưng trình biên dịch không biết điều đó.
Tuy nhiên, nếu chúng tôi đã tìm thấy UTxO, thì vì chúng tôi đã có id và giao dịch của nó, chúng tôi chỉ cần tìm giá trị Integer
của nó. Phần này vẫn có thể sai. Mặc dù chúng tôi đã tìm thấy đúng UTxO, nhưng có thể có một số dữ liệu bị hỏng trong đó vì bất kỳ lý do gì.
Chúng tôi sử dụng hàm oracleValue
mà chúng tôi cũng đã sử dụng để xác thực. Hàm này nhận một tham số TxOut
theo sau là một tham số thứ hai là một hàm, được cung cấp một hàm băm dữ liệu sẽ trả về giá trị dữ liệu được liên kết.
Trong mã off-chain, chúng ta có thể sử dụng tham số hàm sau
Đây txData
là một trường của giao dịch và nó là một map từ hàm băm datum
. Chúng tôi nhận được giao dịch từ txOutTxTx o
.
Nếu tất cả điều này thành công, khi nào sẽ trả về bộ ba (oref, o, x)
,trong đó x
là giá trị Integer
của oracle.
Bây giờ chúng ta đã viết hàm findOracle
. chúng ta có thể nhìn vào hàm updateOracle
.
sau đó nhìn hàm findOracle
hàm trợ giúp, vì chúng ta sẽ cần ràng buộc này hai lần.
Sau khi tìm kiếm Oracles, có rất nhiều khả năng xảy ra - chúng tôi đã tìm thấy nó hoặc chúng tôi không. tìm thấy
Nếu chúng tôi không tìm thấy nó, thì chúng tôi đã bắt đầu Oracles nhưng chúng tôi chưa cung cấp giá trị ban đầu. Đây là trường hợp đầu tiên. Và trong trường hợp này, tất cả những gì chúng ta phải làm là gửi một giao dịch tạo ra giá trị đầu tiên cho oracle.
Đây là cách sử dụng đầu tiên của hàm trợ giúp c
. Nó cung cấp ràng buộc mustPayToTheScript
đảm bảo rằng giao dịch sẽ có đầu ra trả cho một địa chỉ tập lệnh. Là các đối số, nó lấy datum x
và NFT. Tập lệnh mà nó phải trả tiền luôn là tập lệnh nằm trong tiêu điểm - ở đây nó là tham số đầu tiên đến submitTxConstraints
- (oracleInst oracle)
.
Sau đó chúng tôi chờ xác nhận và viết thông báo nhật ký. Và đây là tất cả những gì chúng ta cần làm cho trường hợp này.
Trong trường hợp khác, khi chúng tôi đã có một giá trị, chúng tôi cần tham chiếu đến các phần UTxO, nhưng chúng tôi không quan tâm đến dữ liệu hiện tại, vì chúng tôi vẫn sẽ cập nhật nó.
Bây giờ nó trở nên phức tạp hơn một chút, bởi vì bây giờ chúng ta cần hai điều kiện.
Ràng buộc đầu tiên cũng giống như trong trường hợp khác - ràng buộc được tham chiếu bởi hàm trợ giúp c
. Nhưng bây giờ có một hạn chế bổ sung là chúng ta cũng phải sử dụng UTxO hiện có.
Hàm mustSpendScriptOutput
cơ bản là trái ngược với mustPayToTheScript
. ItNó tạo ra một đầu vào cho địa chỉ tập lệnh này. Vì các tham số, nó có tham chiếu đến UTxO mà chúng ta muốn sử dụng, và nó cần một Redeemer
. Trong trường hợp Redeemer
là Update
nó được chuyển đổi thành kiểu Data
trong Plutus.
Để điều này hoạt động, chúng tôi cần cung cấp một số tra cứu.
Nhằm mục đích tìm đầu ra oref
mà nó muốn chi tiêu, chúng ta phải sử dụng unspentOutputs
tra cứu và trong trường hợp này, chúng ta chỉ cung cấp tra cứu bằng một UTxO.
Sau đó, chúng tôi phải cung cấp các phiên bản script. Chúng ta cần làm điều này hai lần, một lần cho phía đầu vào và một lần cho phía đầu ra. Đối với điều này, chúng tôi cung cấp phiên bản oracle và trình xác thực oracle.
Chúng tôi không cần cung cấp scriptInstanceLookups
tra cứu trong trường hợp đầu tiên, vì chúng tôi có thể chuyển oracleInst oracle
đến hàm submitTxConstraints
. Tuy nhiên, với hàm submitTxConstraintsWith
thì chúng ta không có tùy chọn đó.
Khi gửi giao dịch, chúng ta cần thúc đẩy trình biên dịch một chút để cho nó biết script mà chúng ta đang nói đến - để nó biết, chẳng hạn như Script
ở trong mustPayToTheScript
. Đối với điều này, chúng tôi tham khảo loại Oracling
.
Hy vọng rằng bây giờ chúng ta có một giao dịch hợp lệ được gửi đi, và sau đó chúng ta đợi nó được xác nhận và viết một số thông tin ghi vào nhật ký.
Hãy nhớ rằng, chúng ta đã nói về việc thu phí trước đó. Điều này sẽ tự động xảy ra. Hàm submitTxConstraintsWith
sẽ gửi phí đến ví của chính chúng ta. Nó làm được điều này bởi vì có sự mất cân bằng giữa giá trị gắn với đầu vào, bao gồm phí và NFT, và giá trị mà chúng tôi đã nói phải được trả cho tập lệnh, đó chỉ là NFT.
Quá trình này cũng sẽ tự động tạo thêm một đầu vào từ đầu vào của chính chúng ta để trả phí giao dịch cho việc thực hiện giao dịch.
Cuối cùng, chúng tôi cung cấp một chức năng kết hợp hai hoạt động này startOracle
và updateOracle
thành một hợp đồng. Điều này sẽ làm cho nó có thể được sử dụng trong sân chơi và EmulatorTrace
monad, cũng như trong PAB.
Trước tiên hàm runOracle
khởi động oracle. sau đó, vì những lý do sẽ trở nên rõ ràng sau này, chúng tôi sử dụng tell
để viết tham số cho oracle. Chúng ta cần có khả năng giao tiếp giá trị tham số của oracle với thế giới bên ngoài, để mọi người có thể sử dụng oracle. Chúng tôi sẽ không biết cho đến khi chạy ký hiệu tiền tệ sẽ được sử dụng cho NFT, vì vậy chúng tôi chưa biết giá trị của tham số oracle.
Hãy nhớ rằng tell
mong đợi loại Monoid
. Ví dụ điển hình là một danh sách các chuỗi được nối với một danh sách tất cả các thông báo nhật ký.
Nhưng nó không phải là danh sách. Trong Data.Monoid
Chúng tôi có Last
Monoid.
Chúng ta thấy rằng nó chỉ là một cái newtype
bao bọc xung quanh Maybe
. Vấn đề là cung cấp một ví dụ cụ thể Monoid
. Ý tưởng, như tên cho thấy, đó là một hoạt động đơn nguyên luôn ghi nhớ giá trị Just
cuối cùng.
Ví dụ:
Tuy nhiên, nếu cái thứ hai Last
là không có gì, nó sẽ trả về cái đầu tiên.
Nếu cả hai đều Nothing
, nó sẽ là Nothing
.
Last
rất hữu ích vì nó cho phép chúng ta giữ nguyên trạng thái hiện tại. Giá trị của nhật ký về cơ bản sẽ là giá trị cuối cùng Just
mà chúng tôi đã nói.
Trong hợp đồng này, chúng tôi sẽ chỉ làm điều đó một lần. Ban đầu nó sẽ như vậy Last Nothing
. Sau đó, chúng tôi đúc NFT, và sau đó, khi chúng tôi nhận được giá trị oracle trong runOracle
, và sau đó tell
nó luôn là giá trị đó. Nếu các hợp đồng khác từ bên ngoài truy vấn trạng thái, chúng sẽ luôn nhận được Just oracle
, vì vậy chúng sẽ có thể khám phá giá trị của oracle.
Vì vậy, tiếp theo trong runOracle
, chúng tôi gọi hàm trợ giúp go
. Điều này làm là chặn endpoint update. Ngay sau khi ai đó cung cấp một Integer
như là một giá trị mới, nó sẽ gọi hàm updateOracle
với một giá trị mới, sau đó lặp lại để cho phép người khác cập nhật oracle..
Tóm lại, runOracle
khởi động oracle
, tell
là oracle
, sau đó lặp lại để cho phép người khác cập nhật oracle.
Và điều đó kết thúc mã cho chính nhà Oracles. Những gì hiện còn thiếu là một ví dụ, một hợp đồng thực sự sử dụng oracle - một hợp đồng hoán đổi. Và cũng sử dụng Plutus Application Backend để chạy mã này trong thế giới thực hoặc trong trường hợp của chúng tôi là trong một chuỗi khối mô phỏng.
#
Xác thực hoán đổi(Swap Validation)Hợp đồng hoán đổi mẫu của chúng tôi có thể được tìm thấy trong:
Mục đích của hợp đồng này là để ai đó có thể ký gửi ADA và đổi nó lấy token, trong trường hợp này là token mà chúng tôi sử dụng sẽ gọi là USDT là token stablecoin.
Ý tưởng là giá, số lượng USDT sẽ được yêu cầu thanh toán cho ADA, sẽ được xác định bởi giá trị của oracle. Hãy nhớ rằng chúng tôi đang sử dụng một Integer
để phản ánh tỷ giá hối đoái, với giá trị một triệu tương đương với một USDT.
Chúng ta sẽ bắt đầu với một hàm trợ giúp được gọi là price
, với một số lovelace và tỷ giá hối đoái, sẽ trả về giá USDT.
Tiếp theo là hàm trợ giưps lovelaces
, kết hợp với các hàm từ thư viện Plutus để trích xuất một số lovelace từ một kiểu Value
.
Bây giờ chúng ta sẽ viết mkSwapValidator
. Đây là một trình xác thực được tham số hóa với hai tham số.
Tham số đầu tiên là oracle mà chúng ta đang sử dụng. Để sử dụng điều này, chúng tôi nhập mô-đun oracle.
Tham số thứ hai là địa chỉ của oracle. Thông thường, với oracle , chúng ta sẽ có thể tính toán địa chỉ từ nó. Trong mô-đun cốt lõi, chúng tôi đã thấy một hàm oracleAddress
thực hiện điều này cho chúng tôi. Nhưng đây là một chức năng mà chúng tôi không thể sử dụng trong trình xác thực, vì nó không thể được biên dịch sang tập lệnh Plutus. Vì vậy, ở đây, chúng tôi đưa địa chỉ một cách rõ ràng cho trình xác thực.
Đối với datum, chúng tôi sử dụng mã băm khóa công khai của người bán. Chúng tôi không sử dụng Redeemer, vì vậy chúng tôi cung cấp cho nó một loại Unit
.
Chúng tôi nhớ lại từ sơ đồ, giao dịch hoán đổi phải có ba đầu vào và ba đầu ra.
Hoán đổi đầu vào và đầu ra của giao dịch
Inputs | Outputs |
---|---|
Oracle, kiểm tra tỉ giá hối đoái hiện | Oracle,mà chúng ta không cần phải xem xét trong xác nhận hoán đổi |
UTxO hoán đổi | Các token lovelace của người bán |
Nguồn tiền của người mua | lovelace cho người mua |
Lưu ý rằng chúng ta không cần phải lo lắng về oracle như một đầu ra. Trình xác thực oracle sẽ đảm bảo rằng giá trị không bị thay đổi và các khoản phí được thêm vào.
Chúng tôi cũng muốn hỗ trợ trường hợp sử dụng thứ hai, trường hợp người bán có thể lấy token ADA trong trường hợp họ không muốn thực hiện hoán đổi nữa. Nếu chúng tôi không hỗ trợ trường hợp này, ADA có thể bị khóa ở đó mãi mãi, nếu không ai quyết định thực hiện hoán đổi.
Trường hợp thứ hai này là điều kiện chúng tôi kiểm tra trong trình xác thực. Nếu người bán tự mình ký vào giao dịch, thì không có bất kỳ ràng buộc nào nữa - chúng tôi không cần kiểm tra oracle hay bất cứ thứ gì khác - người bán chỉ có thể lấy lại lovelace của họ.
Trường hợp thú vị hơn là trường hợp thứ hai, nơi chúng tôi kiểm tra hai điều kiện.
Đầu tiên, phải có hai đầu vào - oracle và UTxO hoán đổi. Tất cả các đầu vào bổ sung (tiền của người mua) phải là đầu vào khóa công khai. Điều này là do chúng tôi không muốn lo lắng về việc can thiệp vào các hợp đồng thông minh khác.
Thứ hai, chúng tôi muốn kiểm tra xem người bán có được thanh toán hay không.
Bây giờ, chúng ta có các định nghĩa về hàm trợ giúp của chúng ta.
Đầu tiên:
Sau đó, chúng ta phải dùng oracleInput
để lấy UTxO từ oracle.
Chúng tôi thực hiện điều này bằng cách lấy danh sách tất cả các đầu vào. Đối với điều này, chúng tôi sử dụng khả năng hiểu danh sách, cho phép chúng tôi lấy từ các danh sách khác bằng bộ lọc. Trong trường hợp này, chúng tôi lấy từ danh sách từ txInfoInputs info
, đó là danh sách của TxInfo
. Chúng tôi sử dụng hàm txInInfoResolved
để xem mỗi phần tử kiểu TxOut
, sau đó chúng tôi so sánh với tham số addr
. Danh sách kết quả sẽ trống hoặc sẽ có danh sách TxOut
phù hợp với UTxO oracle.
Sau đó, chúng tôi kiểm tra xem có chính xác một phần tử trong danh sách kết quả hay không, và nếu có, chúng tôi trả về nó. Chúng tôi không trả lại danh sách, chỉ trả lại TxOut
.
Điều này hiện đã cho chúng ta kết quả đầu ra kỳ diệu mà chúng ta đang sử dụng làm đầu vào.
Bây giờ, chúng tôi muốn kiểm tra tỷ giá hối đoái thực tế. Đối với điều đó, chúng tôi sử dụng hàm oracleValue
mà chúng tôi đã xác định trong mô-đun cốt lõi. Ở đây một lần nữa, nó có thể thành công, hoặc nó có thể thất bại. Nếu nó thành công, chúng tôi trả về giá trị.
Chúng ta không cần kiểm tra xem oracle có chứa NFT hay không. Do cách thức hoạt động của xác thực đối với oracle, chúng ta biết rằng nó hiện diện..
Bây giờ, chúng ta hãy xem xét hàm trợ giúp hasTwoScriptInputs
.
Đầu tiên, chúng tôi lọc, sử dụng hàm tổng hợp
Đọc từ phải sang trái, chúng ta nhận được UTxO từ đầu vào, sau đó chúng ta nhận được địa chỉ cho UTxO này, và chúng ta nhận được mã băm của trình xác thực cho địa chỉ đó. Cuối cùng, chúng ta kiểm tra xem nó có phải là đầu ra tập lệnh hay không, bằng cách xem nó có phải là Just
. Nếu nó là Nothing
, thì điều này sẽ cho thấy rằng nó là một khóa công khai, không phải là một địa chỉ tập lệnh.
Sau đó, chúng tôi sử dụng hàm tổng hợp này như một bộ lọc dựa trên danh sách các TxInInfos
. Và sau đó chúng tôi kiểm tra độ dài của danh sách kết quả là chính xác hai.
Quay trở lại các điều kiện xác thực của chúng ta, bây giờ chúng ta phải làm với việc kiểm tra xem người bán có được thanh toán hay không. Vì vậy, hãy viết hàm sellerPaid
mà chúng ta đã tham chiếu.
Đối với điều này, chúng tôi sẽ sử dụng một hàm trợ giúp khác để xác định giá yêu cầu.
Trước tiên, chúng tôi kiểm tra xem chúng tôi có một đầu vào hay không, và nếu có, chúng tôi trích xuất số lượng khoảng cách và gán số đó cho lovelaceIn
. Sau đó, chúng tôi sử dụng hàm price
để xác định giá bằng token USDT.
Bây giờ, chúng ta có thể định nghĩa hàm sellerPaid
.
Hàm valuePaidTo
này là từ các thư viện Plutus. Đã cho info
và một băm khóa công khai, nó sẽ cộng tất cả các giá trị của tất cả các đầu ra khóa công khai đi đến địa chỉ này. Sau đó, chúng tôi sử dụng assetClassValueOf
để kiểm tra thành phần của giá trị có trong token USD và kiểm tra xem chúng tôi có ít nhất bao nhiêu tùy theo yêu cầu của chúng tôi.
Đó là chức năng cuối của phần chính trong mã trình xác thực hoán đổi.
Lưu ý rằng trong hàm swapInst
, nơi chúng ta sử dụng hàm băm mẫu để tạo trình xác thực Plutus từ hàm mkSwapValidator
, chúng tôi không cần chuyển địa chỉ oracle làm tham số. Điều này là do chúng tôi sẽ tính toán điều này bên trong hàm. Hãy nhớ rằng chúng ta không thể sử dụng hàm oracleAddress
bên trong trình xác thực Plutus.
Bây giờ để xác định một số hợp đồng.
#
Chào hàng (offerSwap)Đầu tiên offerSwap
. Điều này dành cho người bán muốn đưa ra một số lượng nhất định để trao đổi.
#
findSwapsTiếp theo, một hàm trợ giúp sẽ tìm tất cả các hoán đổi thỏa mãn một vị thế nhất định. Nó cần một oracle cộng với một vị thế dựa trên hàm băm khóa công khai và trả về danh sách bộ ba UTxO thỏa mãn vị thế.
Đầu tiên, chúng tôi nhận được danh sách tất cả các UTxO ở địa chỉ hợp đồng hoán đổi. Sau đó chúng tôi áp dụng mapMaybe
dành cho danh sách này.
Hàm này sẽ áp dụng hàm (a -> Maybe b)
cho từng phần tử trong danh sách a
và tạo danh sách Maybe b
, có thể chứa hỗn hợp của Just
và Nothing
. sau đó nó bỏ Nothing
và chỉ để lại giá trịJust
.
Để làm rõ điều này, hãy tưởng tượng chúng ta có một hàm trả về như Just
cho số chẵn và một Nothing
cho số lẻ.
Chúng ta có thể sử dụng thông số này làm tham số đầu tiên để lập mapMaybe
Chung ta sử dụng mapMaybe
và hàm g
để lọc danh sách các UTxO.
Hàm này nhận một cặp giá trị khóa đại diện cho UTxO và trả về một bộ ba Maybe
chứa các mục từ cặp cùng với PubKeyHash
.
Hàm g
bên trong monad Maybe
và sử dụng hàm f
,
cũng nằm bên trong monad Maybe
. Hàm f
lấy mã băm khóa công khai từ UTxO, nếu nó tồn tại. Sau đó, hàm g
sử dụng hàmguard
cùng với thuộc tính p
tmà chúng ta chuyển vào làm đối số.
Hàm guard
là hàm có sẵn trong monads, và monad Maybe
cũng vậy. Nó nhận một boolean làm tham số, và nếu boolean là false, thì quá trình tính toán sẽ không thành công. Trong trường hợp này, false có nghĩa là quay trở lại Nothing
. nếu là true, Nếu nó là sự thật, nó chỉ tiếp tục. Trong trường hợp này, điều đó có nghĩa là trả về bộ ba Just
chứa hàm băm khóa công khai.
Chúng ta sẽ xem cách chúng ta sử dụng hàm findSwaps
một lát nữa.
#
lấy lại Hoán đổi (retrieveSwaps)Contract retrieveSwaps
là dành cho người bán nếu họ muốn thay đổi suy nghĩ của họ và nhận được Ada trở lại của họ.
Đây là nơi chúng tôi sử dụng hàm findSwaps
. Chúng ta sử dụng nó với(== pkh)
như là một thuộc tính, có nghĩa là chúng tôi muốn chỉ những UTxO ở địa chỉ hoán đổi thuộc về nhà điều hành.
Nếu không có, thì không có gì để làm. Nếu có một số, chúng tôi xây dựng một giao dịch truy xuất tất cả chúng.
Để làm điều đó, chúng tôi tạo một danh sách các ràng buộc mustSpendScriptOutput
.
Trông có vẻ đáng sợ, nhưng nó chỉ là trích xuất một danh sách các oref
từ danh sách xs
và sử dụng nó để xây dựng một ràng buộc cho từng người trong số họ, sử dụng Unit
như là kiểu Redeemer
. Hàm mconcat
áp dụng Semigroup
toán tử <>
trong toàn bộ danh sách để kết hợp chúng.
Khi tra cứu, chúng tôi phải cung cấp tất cả các UTxO và trình xác thực hoán đổi.
Chúng tôi có danh sách các UTxO trong xs
và chúng tôi biến danh sách này thành danh sách các cặp và sau đó chúng tôi sử dụng Map.fromList
để biến các cặp đó thành một bản đồ (map), sau đó chúng tôi áp dụng ràng buộc unspentOutputs
.
#
useSwapsVà bây giờ là một trong những thú vị nhất useSwaps
. Đây là nơi chúng tôi thực sự sử dụng oracle.
Đầu tiên, chúng tôi sử dụng hàm ownFunds
. Điều này được định nghĩa trong một mô-đun riêng biệt mà chúng ta sẽ đi sâu vào trong một chút. Tất cả những gì nó làm là thêm tất cả tiền vào ví của chính chúng ta và trả về Value
. Sau đó, chúng tôi tìm hiểu chúng tôi có bao nhiêu USD Token.
Hàm findOracle
được định nghia trong mô đun Oracle.Core từ trước. Bạn sẽ nhớ lại rằng nó tìm thấy oracle UTxO có chứa giá trị oracle.
Nếu chúng tôi không tìm thấy Oracles, chúng tôi sẽ chỉ ghi lại một thông báo về hiệu ứng đó.
Nếu chúng tôi tìm thấy nó, chúng tôi sẽ ghi lại một thông báo với tỷ giá hối đoái hiện tại.
Tiếp theo, chúng tôi kiểm tra khóa công khai của riêng mình và kiểm tra tất cả các giao dịch hoán đổi có sẵn.
Sau đó, chúng tôi sử dụng một hàm find
đó là từ Haskell prelude,Trong module Data.List
. Hàm find
với đối số là một thuộc tính là một danh sách Maybe
trả về một phần tử phù hợp với thuộc tính đó.
Hàm được sử dụng trong vị từ được định nghĩa là hàm trợ giúp f
.
Chúng tôi cung cấp cho nó một số tiền, tỷ giá hối đoái hiện tại và một bộ ba UTxO. Hàm xác định xem có hoán đổi nào rẻ hơn hoặc bằng tham số số tiền hay không.
Bây giờ, chúng tôi đã tìm kiếm một giao dịch hoán đổi mà chúng tôi có thể chi trả. Nếu chúng tôi không tìm thấy một, chúng tôi sẽ ghi lại một thông báo nói như vậy.
Nếu chúng tôi tìm thấy một cái, chúng tôi chỉ lấy cái đầu tiên. Tất nhiên, điều này không thực tế lắm. Trong một ví dụ thực tế, chúng tôi có thể sẽ chỉ định số tiền chính xác mà chúng tôi muốn hoán đổi. Ở đây, chúng tôi chỉ giữ nó đơn giản vì chúng tôi tập trung vào mục tiêu mấu chốt hơn là hoán đổi.
Vì vậy, bây giờ chúng ta xây dựng một giao dịch.
Đây là đầu ra cho Oracles. Nó cũng giống như đầu vào, bao gồm bất kỳ khoản phí nào đã tích lũy ở đó, cộng với khoản phí trong tình yêu mà chúng ta cần phải trả.
Sau đó, chúng tôi tạo một Value
token USD đại diện mà chúng tôi cần thanh toán.
Bây giờ, chúng ta hãy xem xét các ràng buộc.
Ràng buộc đầu tiên là chúng ta phải sử dụng oracle làm đầu vào. Và ở đây chúng ta thấy lần đầu tiên sử dụng Redeemer
. Chúng tôi chưa bao giờ sử dụng Redeemer
này trong chính lõi oracle, vì nhà cung cấp oracle chỉ chịu trách nhiệm cập nhật các giá trị sử dụng Redeemer
.
Ràng buộc thứ hai là sử dụng đầu vào hoán đổi, chỉ sử dụng một trình Unit
cho Redeeemer.
Ràng buộc thứ ba là trả tiền oracle.
Ở đây chúng tôi sử dụng mustPayToOtherScript
, chỉ định tập lệnh oracle, vì bây giờ chúng ta có hai tập lệnh đang hoạt động - tập lệnh oracle và tập lệnh hoán đổi. Như Datum
chúng tôi sử dụng datum tồn tại - wchúng tôi không được thay đổi nó - và
như là Value
chúng tôi sử dụng giá trị v
mà chúng tôi đã tính toán trước đó.
Ràng buộc cuối cùng là chúng tôi phải trả tiền cho người bán - và khoản thanh toán là giá trị p
mà chúng tôi đã tính toán trước đó.
Để tra cứu, chúng tôi phải cung cấp trình xác thực của hợp đồng oracle và hoán đổi, đồng thời chúng tôi phải cung cấp hai UTxO mà chúng tôi muốn sử dụng.
Bây giờ, thông thường - chúng tôi gửi nó, chờ xác nhận, sau đó ghi lại một tin nhắn.
#
Gói hợp đồng (Contract bundle)Điều đó xác định các hợp đồng thô. Bây giờ, chúng tôi cung cấp một gói chứa tất cả chúng.
Đầu tiên, chúng tôi định nghĩa, như mọi khi, một SwapSchema
, xác định các endpoints.
Tiếp theo, chúng ta thấy toán tử select
. Việc sử dụng toán tử này sẽ khiến mã của chúng tôi đợi cho đến khi một trong các điểm cuối được chọn, rồi thực thi mã được liên kết.
Các hàm swap
đệ quy gọi chính nó, cung cấp một lần nữa và một lần nữa lựa chọn cùng một endpoints.
Mã cho bốn endpoint là trình bao bọc cho mã chúng ta đã viết.
Ví dụ offer
, wchúng tôi chặn cho đến khi chúng tôi được cung cấp amt
và sau đó gọi contract offerSwap
.
Đối với endpoints retrieve
và use
,chúng không cần tham số.
cuối cùng là endpoint funds
, nó thì khác một chút. hàm ownFunds
xuất phát từ mô-đun Funds
, chúng ta đã đề cập trước đó, Chung ta thấy, nó chứa một giá trị Value
mà nó sở hứu. sau đó chúng ta tell
giá trị này như một cách báo cáo với thế giới bên ngoài rằng chúng tôi có bao nhiêu.
Trong hmỗi điểm cuối là một trình xử lý lỗ h
. Mỗi điểm cuối được bao bọc bên trong trình xử lý lỗi, trình này chỉ ghi lại lỗi, nhưng không tạm dừng thực thi.
Và điều đó kết thúc ví dụ hoán đổi.
#
Funds ModuleBây giờ chúng ta hãy nhanh chóng xem xét mô-đun Funds
. Đó là một mô-đun ngắn cung cấp hai hợp đồng.
Các hàm ownFunds
được giao nhiệm vụ tổng hợp tất cả các giá trị trong các UTxOs của chúng.
Nó thực hiện điều này bằng cách tra cứu khóa công khai của chúng, sau đó nhận tất cả các UTxO tại địa chỉ khóa công khai đó. Sau đó utxos
là một bản đồ từ các tham chiếu UTxO đến các UTxO.
như map
thực hiện Functor
húng tôi có thể ánh xạ trên bản đồ để thay đổi các yếu tố thành một cái gì đó khác. Trong trường hợp này, chúng tôi thay đổi chúng thành giá trị bằng cách áp dụng hàm tổng hợp txOutValue
. txOutTxOut
.
Các hàm Map.elems
bỏ qua các phím và chỉ cho chúng ta các giá trị. Và, như chúng ta đã thấy trước đây mconcat
khi được cung cấp một kiểu Semigroup
or Monoid
sẽ kết hợp danh sách các giá trị thành một giá trị.
Vì vậy, v
không phải là tổng của tất cả các giá trị của tất cả các UTxO mà chúng ta sở hữu.
Hàm ownFunds
của chúng tôi là một hợp đồng có kiểu trả về giá trị v
.
Hàm ownFunds'
là một biến thể, thay vì trả về giá trị, nó sẽ vĩnh viễn là tell.
Điều này gọi hàm ownFunds
thực hiện liên kết đơn nguyên với hàm tổng hợp tell . Last . Just
wày cho biết giá trị, sau đó nó đợi một vị trí và sau đó gọi chính nó. Vì vậy, mỗi khối, nó ghi giá trị vào nhật ký.
#
TestingBây giờ chúng tôi sẽ viết mã, sử dụng EmulatorTrace
monad, kiểm tra các hợp đồng chúng tôi đã viết.
Mã này có thể được tìm thấy trong mô-đun sau:
Đầu tiên, chúng ta cần xác định một token mà chúng ta có thể kiểm tra. assetSymbol
là một hàm băm tùy ý, dùng được cho các mục đích thử nghiệm.
Lần này chúng ta sẽ sử dụng phiên bản mồi của runEmulatorTraceIO
, lấy thêm hai đối số và cung cấp nhiều chi tiết hơn cho môi trường giả lập.
Đối số đầu tiên để runEmulatorTraceIO'
xác định cách hiển thị các thông báo nhật ký khác nhau. Bằng cách sử dụng def
, chúng tôi đã chọn mặc định, giống như trong phiên bản không có bản quyền.
Lý do chúng tôi đang sử dụng phiên bản mồi là chúng tôi muốn định cấu hình phân phối ban đầu và chúng tôi có thể thực hiện điều đó với đối số thứ hai, mà ở đây chúng tôi đã gắn nhãn emCfg
.
Chúng tôi sử dụng điều này với hàm trợ giúp v
để cung cấp cho mọi người 100 triệu lovelace (100 Ada) and 100 triệu USD Tokens để bắt đầu.
Chúng tôi xác định một hợp đồng trợ giúp checkOracle
, nó sẽ liên tục kiểm tra giá trị oracle và ghi lại nó.
Và bây giờ chúng ta có thể xác định dấu vết của mình.
Đây là tất cả những thứ mà chúng ta đã thấy. Chúng tôi xác định các tham số oracle của mình, đặt mức phí oracle ở mức 1 triệu lovelace và loại tài sản tùy ý của chúng tôi đã xác định trước đó.
Sau đó, chúng tôi bắt đầu một oracle và chờ một slot.
Chúng tôi đã nắm bắt được một xử lý đối với oracle bằng cách sử dụng chức năng trợ giúp được định nghĩa trong mệnh đề where
.
Chúng ta cần điều này vì hợp đồng hoán đổi được tham số hóa với giá trị oracle. Và đây là lý do tại sao chúng tôi sử dụng tell
trong hàm runOracle
.
Chúng tôi sử dụng hàm observableState
để nắm giữ thông tin này. Nếu nó không tồn tại, chúng tôi đợi một vị trí và thử lại. Nếu không, chúng tôi ghi lại nó cho mục đích gỡ lỗi và trả lại nó.
Tiếp theo, chúng tôi sử dụng Wallet 2 để thực thi hàm checkOracle
mà chúng tôi đã thấy trước đó.
Sau đó, chúng tôi khởi tạo oracle với tỷ giá hối đoái là 1,5 Ada và đợi 3 slots.
Bây giờ chúng ta gọi hàm ownFunds'
trên ví 1, 3, 4 và 5 để kiểm tra số dư ban đầu.
Sau đó, chúng tôi bắt đầu hợp đồng hoán đổi trên Ví 3, 4 và 5.
Sau đó, chúng tôi thử một số kịch bản. Đầu tiên, Ví 3 và 4 lần lượt cung cấp 10 và 20 Ada để hoán đổi.
Và bây giờ Ví 5 sử dụng hoán đổi. Nó sẽ chọn một trong hai. Không rõ cái nào, cái nào tìm trước. Hãy nhớ rằng chúng tôi chỉ viết mã để tìm vị trí đầu tiên có giá cả phải chăng. Sau đó, nó sẽ trả USD Token cho nó. Số tiền được trả sẽ phụ thuộc vào giá trị hiện tại của oracle.
Bây giờ, Wallet 1 cập nhật giá trị oracle lên 1,7. Điều này cũng dẫn đến phí tích lũy (1 Ada) được trả cho Ví 1.
callEndpoint @\"update\" h1 1_700_000 void \$ Emulator.waitNSlots 3
Sau đó, Wallet 5 thử lại, lấy phần hoán đổi còn lại, nhưng hiện đang trả một giá USD Token khác.
Sau đó, chúng tôi đặt giá trị oracle thành 1,8.
Điều này sẽ cho phép Wallet 1 thu phí. Giá trị oracle thực sự không cần phải thay đổi để điều này xảy ra.
Ví 3 và 4 hiện đưa ra retrieve
yêu cầu lấy lại bất kỳ khoản tiền nào chưa được mua. Điều này sẽ dẫn đến việc không có tiền được trả lại vì tất cả các khoản hoán đổi đã được sử dụng.
Và đây là toàn bộ code. Vì vậy, hãy chạy nó trong REPL.
#
Test in the REPLSẽ có rất nhiều đầu ra.
Hãy xem xét một số phần chính. Đầu tiên, slot 3, nơi tạo ra oracle. Ở đây chúng tôi nhận được giá trị oSymbol
mà chúng tôi có thể sử dụng cho mọi thứ khác.
Chúng tôi cũng thấy trong vị trí số 3 getOracle
được bắt đầu sẽ ghi lại giá trị của oracle, để biết thông tin của chúng tôi, mọi vị trí kể từ bây giờ.
Và, vị trí thứ 4, chúng ta thấy giá trị đầu tiên getOracle
tìm thấy là 1.500.000.
Vị trí 6 là kết quả của tất cả các lần gọi activateContractWallet
. Chúng không nhất thiết phải xuất hiện theo thứ tự như trong mã. Lưu ý rằng Ví 1 có ít Ada hơn một chút so với các ví khác. Điều này là do Ví 1 startOracle và cần phải trả phí giao dịch cho việc đó.
Slot 7, các ưu đãi của 10 Ada và 20 Ada được thực hiện.
Trong vị trí 9, cái đầu tiên use
được yêu cầu.
Và ở vị trí số 10, chúng ta thấy sự hoán đổi xảy ra.
The oracle nhận và cập nhật yêu cầu trong slot 12.
Và chúng tôi thấy nó xảy ra ở vị trí số 13.
Và sự hoán đổi xảy ra ở vị trí 16.
Và ở dưới cùng, chúng tôi thấy số dư cuối cùng.
Ví 2 vẫn có tất cả tiền của nó. Tất cả những gì Ví 2 đã làm là kiểm tra bằng Oracle, không tốn bất kỳ chi phí nào, vì nó hoàn toàn là một vấn đề off-chain.
Ví 1 đã trả một số phí giao dịch nhưng kết thúc với số tiền cao hơn khoảng 2 Ada so với ban đầu. Điều này là do nó đã thu 2 Ada phí sử dụng oracle.
Ví 3 và 4 đều đưa ra đề nghị và số dư của chúng phản ánh tỷ giá hối đoái mà tại đó các đề nghị của chúng đã được chấp nhận.
Ví 5 là người chấp nhận các đề nghị và Ada bổ sung cũng vậy, nhưng số dư token USD bị giảm. Lưu ý rằng Ví 5 cũng đã bị khấu trừ một số khoản phí từ số dư Ada của nó.
Và cuối cùng, cũng như các ví, chúng ta thấy rằng oracle vẫn tiếp tục, và vẫn sở hữu NFT. Lưu ý rằng, trong nhật ký này, chúng tôi không thấy giá trị datum.
#
Plutus Application Backend (PAB)Ngoài ý tưởng về cách triển khai một oracle trong Plutus, không có gì chúng tôi đã làm trong bài giảng này cho đến nay là mới, ngoại trừ một vài hàm thư viện và kỹ thuật Haskell. Về nguyên tắc, chúng ta đã quen thuộc với cách trình xác thực được viết cho mã off-chain và cách hợp đồng được viết cho mã on-chain cũng như cách chúng ta có thể kiểm tra mã của mình với EmulatorTrace
monad.
Nhưng bây giờ chúng ta sẽ nói về một thứ mới - Plutus Application Backend (PAB), cho phép chúng ta lấy tất cả những thứ chúng ta đã làm và biến nó thành một tệp thực thi chạy các hợp đồng.
Nếu testnet hoặc mainnet có sẵn với sự hỗ trợ của Plutus, chúng tôi có thể triển khai một ứng dụng như vậy, nhưng hiện tại chúng tôi sẽ cần hài lòng với một blockchain mô phỏng. Tuy nhiên, quá trình biến mã thành một dApp thực tế giống như trên một blockchain thực.
Chúng ta cần thêm một mô-đun nhỏ nữa, về cơ bản chỉ là một định nghĩa kiểu. Nó rất nhỏ, chúng tôi có thể bao gồm toàn bộ nội dung tệp ở đây.
Ý tưởng là nó sửa đổi các phiên bản hợp đồng mà chúng tôi muốn chạy. Chúng tôi có nhiều hợp đồng khác nhau và chúng tôi muốn có một kiểu dữ liệu trong đó mỗi giá trị của kiểu dữ liệu tương ứng với một liên hệ mà cuối cùng chúng tôi muốn chạy.
Phương thức khởi tạo Init
sẽ được sử dụng để thiết lập một môi trường nơi có sẵn token USD và nơi các ví có nguồn cung cấp ban đầu của chúng.
Hàm Oracle
tạo tương ứng với runOracle
hợp đồng sẽ bắt đầu oracle
và cung cấp endpoint update
, và tham số CurrencySymbol
sẽ được sử dụng cho token USD.
Cuối cùng, các Swap, tham số hóa bằng cách Oracle
sẽ được sử dụng để chạy các hợp đồng hoán đổi, cung cấp thiết bị đầu cuối khác nhau như offer
, retrieve
, use
và funds
.
Chúng ta cần định nghĩa OracleContracts
trong một mô-đun riêng biệt vì chúng ta sẽ sử dụng nó cả từ PAB và cả từ giao diện người dùng.
Chúng ta sẽ xem xét tệp Cabal.
Trong đó, chúng tôi có các định nghĩa cho các tệp thực thi khác nhau.
Tập tin oracle-pab
thực thi sẽ thiết lập một ví mô phỏng, khởi tạo tất cả các hợp đồng và thiết lập một máy chủ web cho phép thế giới bên ngoài tương tác với các hợp đồng này.
Tệp oracle-clientt
thực thi sẽ được chạy bởi nhà cung cấp oracle, do đó, tệp sẽ tương tác với hợp đồng runOracle
. Nó cũng sẽ lấy tỷ giá hối đoái từ internet và đưa chúng vào hệ thống.
Sau đó, tệp có swap-client
thực thi sẽ được chạy bởi các khách hàng muốn sử dụng hợp đồng hoán đổi.
Chúng ta sẽ lần lượt xem xét từng điều này.
Bạn có thể tìm thấy mã cho từng ứng dụng này trong thư mục app
.
#
Oracle PABĐầu tiên, một số bảng soạn sẵn để kết nối kiểu dữ liệu mà chúng ta vừa xác định - các trường hợp hợp đồng được sửa đổi - với các lược đồ và hợp đồng mà chúng ta đã xác định trước đó.
Init
sẽ không có bất kỳ schema nào ,vì vậy nó chỉ có BlockChainActions
.
Oracle
sử dụng OracleSchema
và Swap
sử dụng SwapSchema
. Không có gì ngạc nhiên ở đó.
Init
sẽ chạy initContract
, mà chúng ta sẽ thấy trong giây lát.
Oracle
sẽ chạy hợp đồng runOracle
với oracleParams
ký hiệu tiền tệ của USD Token và ví dụ định nghĩa oracleparams.
và cuối cùng Swap
sẽ chạy hợp đồng hoán đổi với một giá trị oracle.
Đây là một số bản copy/paste boilerplate.
Và đây là hàm initContract
mà chúng tôi đã đề cập ngay trước đây.
Hàm initContract
đúc Tokens USD phân phối chúng đến các ví, sau đó nó telllà biểu tượng tiền tệ cho Token USD .
Ví được mã hóa cứng trước đó trong mã. Số lượng ví và số token được trao cho chúng là hoàn toàn tùy ý.
Bây giờ chúng ta có thể nhìn vào mã PAB thực tế.
Lần đầu tiên, chúng ta thấy một hàm main
là điểm nhập của tệp thực thi.
Các hàm main
sử dụng monad khác mà chúng ta chưa từng thấy trước đây và là đặc trưng cho PAB - Các Simulator
monad.
Cái Simulator
monad là rất giống với EmulatorTrace
monad. Về nguyên tắc nó có các khả năng như nhau. Bạn có thể bắt đầu các hợp đồng trên ví, bạn có thể kiểm tra trạng thái bằng cách sử dụng nhật ký, bạn có thể gọi các endpoint, v.v.
Có một chút đáng tiếc là có hai monads cho điều này vì chúng rất giống nhau. Nhóm Plutus có kế hoạch sắp xếp chúng và có thể biến chúng thành một. Vì vậy, nó có thể không đáng để tìm hiểu những điều phức tạp của Simulator
monad vì nó có thể sẽ sớm thay đổi.
Tương tự như runEmulatorTraceIO
,Chúng ta có runSimulationWith
để pass qua handlers
boilerplate.
Tuy nhiên một sự khác biệt EmulatorTrace
monad:
EmulatorTrace
monad là mã thuần túy - không có tác dụng phụ trong thế giới thực, không liên quan đến IO. Đặc biệt có một trình thông dịch thuần túy runEmulatorTrace
là một hàm Haskell thuần túy không có tác dụng phụ.
Simulator
khác - bạn có thể làm IO. Cách nó hoạt động là đang sử dụng MonadIO
, trong đó có một phương thức liftIO
, thực hiện một hành động IO
và lifts
nó trở thành monad đề cập . Vì vậy, nếu bạn có một số hành động IO tùy ý mà bạn có thể thực hiện trong Haskell, thì bằng cách áp dụng liftIO
cho nó, bạn có thể chuyển nó vào Simulator
monad.
Ngoài ra, nếu bạn nheo mắt, nó trông rất giống với một EmulatorTrace
.
Điều đầu tiên chúng tôi làm là sử dụng logString
ể ghi lại rằng chúng tôi đang khởi động máy chủ PAB. Sau đó, chúng tôi gọi hàm startServerDebug
, à giá trị trả về của hàm đó được liên kết với shutdown
ó thể được sử dụng sau này để tắt máy chủ.
Bây giờ chúng tôi sử dụng một cái gì đó được gọi activateContract
là tương đương với activateContractWallet
từ EmulatorTrace
monad.
Nó cần một ví mà chúng ta muốn bắt đầu phiên bản đó và sau đó là một giá trị của loại hợp đồng đã được sửa đổi. Hãy nhớ rằng chúng ta đã liên kết tạo hàm Init
với hàm initContract
.
Bây giờ chúng ta cần ký hiệu tiền tệ. Đây là một ví dụ về cách chúng tôi lấy thông tin ra khỏi hợp đồng bằng cách sử dụng tell
.
Hàm cidInit
sử dụng một hàm từ monad Simulator
được gọi là
waitForState
, hàm này nhận một hợp đồng và một vị từ. Vị từ nhận một biểu thức JSON và trả về Maybe a
.
Ý tưởng là nó sẽ đọc trạng thái của hợp đồng mà chúng tôi đã viết bằng cách sử dụng tell
. Điều này được tuần tự hóa dưới dạng giá trị JSON và nó áp dụng giá trị JSON này cho vị từ (predicate) được cung cấp. Nếu kết quả là Nothing
, nó chỉ cần đợi cho đến khi trạng thái thay đổi một lần nữa. Nhưng, nếu đúng như vậy Just x
nó sẽ trả về x
.
Có thể có hai cách Nothing
- phân tích cú pháp JSON có thể không thành công hoặc chúng ta có thể nhận được Last Nothing
. Vì vậy, kết quả cuối cùng là hàm đợi cho đến khi trạng thái của hợp đồng cho biết một giá trị Just
.
Tại thời điểm này, chúng tôi có ký hiệu tiền tệ bị ràng buộc cs
. Và sau đó chúng tôi chờ đợi cho đến khi initContract
kết thúc.
Bước tiếp theo là bắt đầu oracle trên Ví 1, sử dụng giá trị cs
mà chúng tôi đã thu được gần đây.
Để tương tác với hợp đồng oracle từ thế giới bên ngoài, ví dụ như từ giao diện web, chúng ta cần bắt tay vào xử lý cidOracle
.
Vì vậy, những gì chúng tôi làm là ghi điều này vào một tệp có tên oracle.cid
.
Điều này chỉ là nhanh chóng và không tốt để trình diễn. Trong mã sản xuất, bạn sẽ sử dụng một cơ chế an toàn hơn.
Bây giờ chúng tôi sử dụng waitForLast
một lần nữa để nhận giá trị oracle, mà chúng tôi đã cung cấp từ hợp đồng runOracle
thông qua tell
. Chúng ta cần điều này vì hợp đồng hoán đổi được tham số hóa bởi giá trị này.
Ở giai đoạn này, NFT được đúc và chúng ta biết giá trị oracle là gì.
Tiếp theo, chúng tôi lặp qua tất cả các ví, ngoại trừ ví 1 chạy oracle và chúng tôi kích hoạt hợp đồng hoán đổi trên mỗi ví. Ở đây chúng tôi sử dụng một phương pháp ghi tệp tương tự để nắm giữ các quy trình xử lý hợp đồng
Bây giờ chúng tôi chỉ chặn cho đến khi người dùng nhấn enter, sau đó chúng tôi tắt máy chủ.
Không thực sự cần thiết phải làm tất cả những điều này, vì bạn cũng có thể bắt đầu và dừng các phiên bản hợp đồng từ giao diện web. Ở đây, chúng tôi dễ dàng thực hiện điều đó hơn theo cách có kịch bản cho bản demo, nhưng về nguyên tắc, bạn chỉ có thể khởi động trình mô phỏng và sau đó đợi cho đến khi bạn tắt máy.
Nếu bạn tò mò về API do PAB cung cấp, bạn có thể kiểm tra điều đó trong gói plutus-pab
, trong mô-đun Plutus.PAB.Webserver.API
. Nhưng cái mà chúng tôi đang sử dụng ở đây là:
Điều này làm cho việc sử dụng thư viện Haskell phổ biến Servant
để viết các ứng dụng web an toàn, nhưng nó sẽ có thể đọc được ít nhiều mà không cần biết về thư viện Servant
. Ví dụ: bạn có thể thấy endpoint /api/new/contract/activate
được khai báo mà bạn có thể dùng ContractActivationArgs
làm phần thân của nó và trả về một ContractInstanceId
.
Ngoài ra còn có một API ổ cắm web, nhưng chúng tôi đã không sử dụng nó trong ví dụ này.
Vì vậy, hãy thử tệp thực thi của chúng tôi.
Chúng tôi nhận được đầu ra nhật ký tương tự như những gì chúng tôi thấy với EmulatorTrace
, nhưng đây bây giờ là một máy chủ trực tiếp.
Đầu ra bên dưới được giảm bớt để tránh đầy đủ chi tiết.
Ví dụ, chúng ta có thể thấy nơi chúng ta có thể tìm thấy ID phiên bản của các hợp đồng nếu chúng ta muốn tương tác với chúng thông qua API.
Nếu bây giờ chúng ta dừng máy chủ và tìm trong thư mục, chúng ta sẽ thấy các tệp mà chúng ta đã lưu trữ các ID phiên bản.
Với thông tin này - thu được từ nhật ký máy chủ web hoặc từ các tệp chúng tôi đã tạo, chúng tôi có thể sử dụng bất kỳ công cụ HTTP nào như Curl hoặc Postman để tương tác với các hợp đồng khi máy chủ web đang chạy. Theo mặc định, nó chạy trên cổng 8080. Chúng tôi cũng có thể viết mã bằng bất kỳ ngôn ngữ lập trình nào mà chúng tôi muốn để tương tác với máy chủ web bằng cách sử dụng các endpoints HTTP.
Bây giờ chúng ta sẽ xem xét ngắn gọn về oracle-client
và swap-client
. Chúng tôi sẽ không đi vào quá nhiều chi tiết vì chúng tôi không quá quan tâm đến cách viết giao diện người dùng ở đây.
#
Oracle ClientChúng tôi sử dụng thư viện Haskell Req
ể tương tác với máy chủ web.
Đầu tiên chúng tôi đọc tệp oracle.cid
để lấy ID phiên bản oracle. Sau đó, chúng ta có một hàm đệ quy go
.
Hàm go
tra cứu tỷ giá hối đoái hiện tại trên CoinMarketCap, kiểm tra xem điều đó đã thay đổi hay chưa, và nếu có thay đổi, nó sẽ gọi hàm updateOracle
này gọi là endpoint updateOracle
trên hợp đồng của chúng tôi. Và, cho dù thay đổi có được phát hiện hay không, nó sẽ đợi trong năm giây (tùy ý), rồi lại tiếp tục.
Thời gian trì hoãn sẽ phụ thuộc vào những điều như giới hạn tỷ lệ do CoinMarketCap áp đặt. Trên thực tế, vì các khối trên Cardano chỉ xuất hiện hai mươi giây một lần, nên năm giây có lẽ là quá ngắn.
Bây giờ, hàm updateOracle
chuẩn bị một yêu cầu POST bằng cách sử dụng ID phiên bản oracle và một phần thân JSON chứa tỷ giá hối đoái.
Vad đây là hàm getExchangeRate
cái mà nhanh chóng và hiệu quả để nhận tỷ giá hối đoái từ CoinMarketCap. Họ cung cấp một API thích hợp, nhưng ở đây chúng tôi chỉ thực hiện một số thao tác quét màn hình từ trang web và sử dụng regex
để trích xuất giá trị mà chúng tôi quan tâm. Tất nhiên, điều này rất mỏng manh và không bao giờ có thể được sử dụng làm mã sản xuất.
Hãy để chúng tôi chạy nó.
Trước tiên, chúng ta cần đảm bảo rằng PAB đang chạy.
Sau đó, trong một thiết bị đầu cuối khác (terminal)
Chúng ta có thể thấy tỷ giá hối đoái mà nó thu được từ CoinMarketCap và yêu cầu cập nhật oracle.
Và nếu chúng ta đợi đủ lâu, chúng ta sẽ thấy
Và chúng tôi thấy rằng chúng tôi đang di chuyển.
Nếu bạn chuyển trở lại PAB, bạn cũng sẽ thấy các thông báo nhật ký bổ sung.
#
Swap ClientThe swap client rất đơn giản.
Ở đây, chúng tôi chỉ đưa ra một giao diện bảng điều khiển đơn giản, vì vậy chúng tôi không bận tâm đến đồ họa hoặc giao diện người dùng web.
Ý tưởng là lấy một lệnh từ bảng điều khiển và gọi endpoint thích hợp. .
Việc gọi endpoint sử dụng cùng một phương thức cho mỗi endpoint, tạo ra một cuộc gọi HTTP giống như cách mà chúng tôi đã làm cho ứng dụng Oracle Client.
Các hàm getFunds
hơi phức tạp hơn so với ba hàm kia vì nó cần để có được thông tin từ máy chủ. Đối với điều này, nó cần phải thực hiện hai yêu cầu. Yêu cầu thứ hai là đọc trạng thái told
của lần gọi đầu tiên.
Hãy chạy ứng dụng Swap client. Chúng tôi sẽ để máy chủ web và ứng dụng oracle client đang chạy.
Khi sử dụng cabal, chúng ta truyền các tham số như sau --
. Đối với ứng dụng oracle client, chúng tôi chuyển số ví vào làm tham số.
Chúng tôi sẽ khởi chạy ứng dụng Swap client cho ví 2 và 3, mỗi ví trong một cửa sổ riêng biệt và truy vấn số tiền tương ứng của chúng.
Ví 2 hiện cung cấp 10 Ada dưới dạng hoán đổi và chúng tôi kiểm tra tiền và chúng tôi thấy rằng số dư Ada đã giảm xuống (bằng 10 Ada cộng với phí giao dịch), nhưng số dư token USD vẫn giữ nguyên.
Trong khi các lệnh này đang chạy, bạn cũng có thể thấy các lệnh gọi đang được thực hiện trong đầu ra PAB.
Now, Wallet 3 is going to take up the offer of the swap. Bây giờ, Wallet 3 sẽ nhận offer của hoán đổi.
Sẽ mất một chút thời gian để cập nhật tiền, vì vậy hãy thử lại lệnh Funds
Cái đó tốt hơn. Và chúng ta có thể thấy rằng ví 3 đã nhận được 10 Ada, trừ đi phí oracle của 1 Ada và trừ đi phí giao dịch.
Trong đầu ra PAB, chúng tôi thấy một cái gì đó như
Hãy xem xét quỹ của ví 2.
Và chúng tôi thấy rằng ví 2 đã mất một số Ada, nhưng đã thu được một số Token USD. Việc hoán đổi đã hoàn tất, sử dụng tỷ giá hối đoái đang diễn ra, ngay bây giờ, đã được đưa vào blockchain giả thông qua oracle.
Vì vậy, bây giờ chúng ta đã thấy một ví dụ end-to-end của Plutus dApp. Nó có giao diện người dùng, nó nói chuyện với thế giới bên ngoài, truy cập internet, lấy thông tin và tương tác với các hợp đồng thông minh của Plutus. Các hợp đồng thông minh gửi các giao dịch tới blockchain nơi logic xác thực bắt đầu và đảm bảo rằng mọi thứ tuân theo các quy tắc kinh doanh.
Trong ví dụ này, vì chúng ta không có blockchain thực để chơi cùng, nên tất cả các ví đều sử dụng cùng một máy chủ PAB, tất nhiên, trong cuộc sống thực sẽ là ngớ ngẩn. Rõ ràng, các ví khác nhau sẽ có các phiên bản PAB chạy khác nhau.
Nhưng, ngoài điều đó ra, nó gần như chính xác là end-to-end, cách một hệ thống như vậy sẽ hoạt động.