Ứng dụng tài khoản thông minh Waves: từ đấu giá đến chương trình thưởng

Ứng dụng tài khoản thông minh Waves: từ đấu giá đến chương trình thưởng

Blockchain thường chỉ gắn liền với tiền điện tử nhưng lĩnh vực ứng dụng công nghệ DLT còn rộng hơn nhiều. Một trong những lĩnh vực hứa hẹn nhất cho việc sử dụng blockchain là hợp đồng thông minh được thực thi tự động và không yêu cầu sự tin tưởng giữa các bên tham gia vào nó.

RIDE – ngôn ngữ cho hợp đồng thông minh

Waves đã phát triển một ngôn ngữ đặc biệt cho hợp đồng thông minh – RIDE. Tài liệu đầy đủ của nó được đặt đây. Và đây - bài viết về chủ đề này trên Habr.

Hợp đồng RIDE là một vị từ và trả về kết quả là “true” hoặc “false”. Theo đó, giao dịch sẽ được ghi lại trong blockchain hoặc bị từ chối. Hợp đồng thông minh đảm bảo đầy đủ việc thực hiện các điều kiện quy định. Hiện không thể tạo giao dịch từ hợp đồng trong RIDE.

Ngày nay có hai loại hợp đồng thông minh Waves: tài khoản thông minh và tài sản thông minh. Tài khoản thông minh là tài khoản người dùng thông thường nhưng có một tập lệnh được đặt cho tài khoản đó để kiểm soát tất cả các giao dịch. Ví dụ: tập lệnh tài khoản thông minh có thể trông như thế này:

match tx {
  case t: TransferTransaction | MassTransferTransaction => false
  case _ => true
}

tx là giao dịch đang được xử lý mà chúng tôi chỉ cho phép sử dụng cơ chế khớp mẫu nếu đó không phải là giao dịch chuyển khoản. Khớp mẫu trong RIDE được sử dụng để kiểm tra loại giao dịch. Tất cả các tài khoản hiện có có thể được xử lý trong tập lệnh tài khoản thông minh các loại giao dịch.

Tập lệnh cũng có thể khai báo các biến, sử dụng cấu trúc “if-then-else” và các phương thức khác để kiểm tra đầy đủ các điều kiện. Để đảm bảo rằng các hợp đồng có tính đầy đủ và độ phức tạp (chi phí) có thể chứng minh được, dễ dự đoán trước khi bắt đầu thực hiện hợp đồng, RIDE không chứa các vòng lặp hoặc câu lệnh nhảy.

Các tính năng khác của tài khoản Waves bao gồm sự hiện diện của “trạng thái”, tức là trạng thái của tài khoản. Bạn có thể ghi vô số cặp (khóa, giá trị) vào trạng thái tài khoản bằng cách sử dụng các giao dịch dữ liệu (DataTransaction). Thông tin này sau đó có thể được xử lý thông qua API REST và trực tiếp trong hợp đồng thông minh.

Mỗi giao dịch có thể chứa một loạt bằng chứng, trong đó có thể nhập chữ ký của người tham gia, ID của giao dịch được yêu cầu, v.v.

Làm việc với RIDE thông qua IDE cho phép bạn xem chế độ xem đã biên dịch của hợp đồng (nếu nó được biên dịch), tạo tài khoản mới và đặt tập lệnh cho nó, cũng như gửi giao dịch qua dòng lệnh.

Để có một chu trình đầy đủ, bao gồm tạo tài khoản, cài đặt hợp đồng thông minh trên đó và gửi giao dịch, bạn cũng có thể sử dụng thư viện để tương tác với API REST (ví dụ: C#, C, Java, JavaScript, Python, Rust, Elixir) . Để bắt đầu làm việc với IDE, chỉ cần nhấp vào nút MỚI.

Khả năng sử dụng hợp đồng thông minh rất rộng: từ việc cấm giao dịch đến một số địa chỉ nhất định (“danh sách đen”) cho đến các dApp phức tạp.

Bây giờ chúng ta hãy xem các ví dụ cụ thể về việc sử dụng hợp đồng thông minh trong kinh doanh: khi tiến hành đấu giá, bảo hiểm và tạo các chương trình khách hàng thân thiết.

Đấu giá

Một trong những điều kiện để cuộc đấu giá thành công là tính minh bạch: người tham gia phải tin tưởng rằng không thể thao túng giá thầu. Điều này có thể đạt được nhờ vào blockchain, nơi dữ liệu bất biến về tất cả các cược và thời gian chúng được thực hiện sẽ có sẵn cho tất cả người tham gia.

Trên chuỗi khối Waves, giá thầu có thể được ghi lại ở trạng thái tài khoản đấu giá thông qua DataTransaction.

Bạn cũng có thể đặt thời gian bắt đầu và kết thúc cuộc đấu giá bằng cách sử dụng số khối: tần suất tạo khối trong chuỗi khối Waves xấp xỉ bằng 60 giây.

1. Đấu giá tăng dần tiếng Anh

Những người tham gia đấu giá ở Anh đặt giá thầu cạnh tranh với nhau. Mỗi lần đặt cược mới phải vượt quá lần đặt cược trước đó. Cuộc đấu giá kết thúc khi không còn người trả giá nào vượt quá giá thầu cuối cùng. Trong trường hợp này, người trả giá cao nhất phải cung cấp số tiền đã nêu.

Ngoài ra còn có một lựa chọn đấu giá trong đó người bán đặt mức giá tối thiểu cho lô hàng và giá cuối cùng phải vượt quá mức giá đó. Nếu không, lô vẫn chưa bán được.

Trong ví dụ này, chúng tôi đang làm việc với một tài khoản được tạo riêng cho phiên đấu giá. Thời gian đấu giá là 3000 khối và giá khởi điểm của lô là 0,001 WAVES. Người tham gia có thể đặt giá thầu bằng cách gửi DataTransaction với khóa “giá” và giá trị giá thầu của họ.

Giá của giá thầu mới phải cao hơn giá hiện tại của khóa này và người tham gia phải có ít nhất mã thông báo [new_bid + Commission] trong tài khoản của mình. Địa chỉ của người đặt giá thầu phải được ghi lại trong trường "người gửi" trong DataTransaction và chiều cao khối giá thầu hiện tại phải trong thời gian đấu giá.

Nếu khi kết thúc phiên đấu giá, người tham gia đã đặt mức giá cao nhất, anh ta có thể gửi ExchangeTransaction để thanh toán cho lô tương ứng với mức giá và cặp tiền tệ được chỉ định.

let startHeight = 384120
let finishHeight = startHeight + 3000
let startPrice = 100000
 
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
 
match tx {
case d : DataTransaction =>
  #проверяем, задана ли в стейте цена
  let currentPrice = if isDefined(getInteger(this, "price"))
 
                      #извлекаем цену из стейта
                      then extract(getInteger(this, "price"))
                      else startPrice
 
  #извлекаем цену из транзакции
  let newPrice = extract(getInteger(d.data, "price"))
  let priceIsBigger = newPrice > currentPrice
  let fee = 700000
  let hasMoney = wavesBalance(tx.sender) + fee >= newPrice
 
  #убеждаемся, что в текущей транзакции два поля и что отправитель совпадает с указанным в транзакции
  let correctFields = size(d.data) == 2 &&      
      d.sender == addressFromString(extract(getString(d.data,"sender")))
  startHeight <= height && height <= finishHeight && priceIsBigger && hasMoney && correctFields
case e : ExchangeTransaction =>
  let senderIsWinner = e.sender == addressFromString(extract(getString(this, "sender"))) #убеждаемся, что лот обменивает тот, кто его выиграл
  let correctAssetPair = e.sellOrder.assetPair.amountAsset == token && ! isDefined(e.sellOrder.assetPair.priceAsset)
  let correctAmount = e.amount == 1
  let correctPrice = e.price == extract(getInteger(this, "price"))
 
  height > finishHeight && senderIsWinner && correctAssetPair && correctAmount && correctPrice
case _ => false
}

2. Đấu giá Hà Lan giảm giá

Trong một cuộc đấu giá ở Hà Lan, ban đầu rất nhiều thứ được đưa ra ở mức giá cao hơn mức giá mà người mua sẵn sàng trả. Giá giảm dần cho đến khi một trong những người tham gia đồng ý mua lô ở mức giá hiện tại.

Trong ví dụ này, chúng tôi sử dụng các hằng số tương tự như trong ví dụ trước, cũng như bước giá khi delta giảm. Tập lệnh tài khoản sẽ kiểm tra xem người tham gia có thực sự là người đầu tiên đặt cược hay không. Mặt khác, DataTransaction không được blockchain chấp nhận.

let startHeight = 384120
let finishHeight = startHeight + 3000
let startPrice = 100000000
let delta = 100
 
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
match tx {
case d : DataTransaction =>
  let currentPrice = startPrice - delta * (height - startHeight)
 
  #извлекаем из поступившей дата-транзакции поле "price"
  let newPrice = extract(getInteger(d.data, "price"))
 
  #убеждаемся, что в стейте текущего аккаунта не содержится поля "sender"
  let noBetsBefore = !isDefined(getInteger(this, "sender"))
  let fee = 700000
  let hasMoney = wavesBalance(tx.sender) + fee >= newPrice
 
  #убеждаемся, что в текущей транзакции только два поля
  let correctFields = size(d.data) == 2 && newPrice == currentPrice && d.sender == addressFromString(extract(getString(d.data, "sender")))
  startHeight <= height && height <= finishHeight && noBetsBefore && hasMoney && correctFields
case e : ExchangeTransaction =>
 
  #убеждаемся, что отправитель текущей транзакции указан в стейте аккаунта по ключу sender
  let senderIsWinner = e.sender == addressFromString(extract(getString(this, "sender")))
 
  #убеждаемся, что аmount ассета указан корректно, и что прайс-ассет - waves
  let correctAssetPair = e.sellOrder.assetPair.amountAsset == token && ! isDefined(e.sellOrder.assetPair.priceAsset)
  let correctAmount = e.amount == 1
  let correctPrice = e.price == extract(getInteger(this, "price"))
  height > finishHeight && senderIsWinner && correctAssetPair && correctAmount && correctPrice
case _ => false
}

3. Đấu giá “trả hết”

“Trả hết” là một cuộc đấu giá trong đó tất cả người tham gia trả giá, bất kể ai thắng nhiều. Mỗi người tham gia mới trả một giá thầu và người tham gia nào đặt giá thầu tối đa sẽ thắng rất nhiều.

Trong ví dụ của chúng tôi, mỗi người tham gia đấu giá đặt giá thầu thông qua DataTransaction với (key, value)* = (“người chiến thắng”, địa chỉ),(“giá”, giá). Giao dịch dữ liệu như vậy chỉ được chấp thuận nếu người tham gia này đã có Giao dịch chuyển tiền có chữ ký của anh ấy và giá thầu của anh ấy cao hơn tất cả những giao dịch trước đó. Cuộc đấu giá tiếp tục cho đến khi đạt đến endHeight.

let startHeight = 1000
let endHeight = 2000
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
match tx {
 case d: DataTransaction =>
   #извлекаем из поступившей дата-транзакции поле "price"
   let newPrice = extract(getInteger(d.data, "price"))
 
   #извлекаем из пруфов транзакции публичный ключ аккаунта
   let pk = d.proofs[1]
   let address = addressFromPublicKey(pk)
 
   #извлекаем транзакцию доказательство из пруфов поступившей дата транзакции
   let proofTx = extract(transactionById(d.proofs[2]))
   
   height > startHeight && height < endHeight
   && size(d.data) == 2
   #убеждаемся, что адрес победителя, извлеченный из текущей транзакции, совпадает с адресом, извлеченным из пруфов
   && extract(getString(d.data, "winner")) == toBase58String(address.bytes)
   && newPrice > extract(getInteger(this, "price"))
   #проверяем, что транзакция подписана
   && sigVerify(d.bodyBytes, d.proofs[0], d.proofs[1])
   #проверяем корректность транзакции, указанной в пруфах
   && match proofTx {
     case tr : TransferTransaction =>
       tr.sender == address &&
       tr.amount == newPrice
     case _ => false
   }
 case t: TransferTransaction =>
 sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
 || (
   height > endHeight
   && extract(getString(this, "winner")) == toBase58String((addressFromRecipient(t.recipient)).bytes)
   && t.assetId == token
   && t.amount == 1
 )
 case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}

Bảo hiểm / Huy động vốn từ cộng đồng

Hãy xem xét một tình huống mà bạn cần bảo hiểm tài sản của người dùng khỏi những tổn thất tài chính. Ví dụ: người dùng muốn có sự đảm bảo rằng nếu mã thông báo mất giá, anh ta sẽ có thể lấy lại toàn bộ số tiền đã trả cho các mã thông báo này và sẵn sàng trả số tiền bảo hiểm hợp lý.

Để thực hiện điều này, cần phải phát hành “mã thông báo bảo hiểm”. Sau đó, một tập lệnh sẽ được cài đặt trên tài khoản của chủ hợp đồng, chỉ cho phép thực thi những Giao dịch trao đổi đáp ứng các điều kiện nhất định.

Để tránh chi tiêu gấp đôi, bạn cần yêu cầu người dùng gửi trước DataTransaction đến tài khoản của chủ hợp đồng với (key, value) = (purchaseTransactionId, sellOrderId) và cấm gửi DataTransactions bằng khóa đã được sử dụng.

Do đó, bằng chứng của người dùng phải chứa ID giao dịch của việc mua mã thông báo bảo hiểm. Cặp tiền tệ phải giống với cặp tiền tệ trong giao dịch mua hàng. Chi phí cũng phải bằng mức cố định tại thời điểm mua trừ đi giá bảo hiểm.

Điều này được hiểu rằng sau đó, tài khoản bảo hiểm sẽ mua mã thông báo bảo hiểm từ người dùng với mức giá không thấp hơn giá mà họ đã mua: tài khoản bảo hiểm tạo Giao dịch trao đổi, người dùng ký đơn đặt hàng (nếu giao dịch được hoàn thành chính xác), tài khoản bảo hiểm ký lệnh thứ hai và toàn bộ giao dịch rồi gửi nó tới blockchain.

Nếu không có giao dịch mua nào xảy ra, người dùng có thể tạo ExchangeTransaction theo các quy tắc được mô tả trong tập lệnh và gửi giao dịch tới blockchain. Bằng cách này, người dùng có thể trả lại số tiền đã chi cho việc mua mã thông báo được bảo hiểm.

let insuranceToken = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
 
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let freezePeriod = 150000
let insurancePrice = 10000
match tx {
 
 #убеждаемся, что, если поступила дата-транзакция, то у нее ровно одно поле и в стейте еще нет такого ключа
 case d : DataTransaction => size(d.data) == 1 && !isDefined(getBinary(this, d.data[0].key))
 case e : ExchangeTransaction =>
 
   #если у транзакции нет седьмого пруфа, проверяем корректность подписи
   if !isDefined(e.proofs[7]) then
     sigVerify(e.bodyBytes, e.proofs[0], e.senderPublicKey)
   else
     #если у транзакции есть седьмой пруф, извлекаем из него транзакцию и узнаём её высоту
     let purchaseTx = transactionById(e.proofs[7])
     let purchaseTxHeight = extract(transactionHeightById(e.proofs[7]))
    
     #обрабатываем транзакцию из пруфа
     match purchaseTx {
       case purchase : ExchangeTransaction =>
         let correctSender = purchase.sender == e.sellOrder.sender
         let correctAssetPair = e.sellOrder.assetPair.amountAsset == insuranceToken &&
                                purchase.sellOrder.assetPair.amountAsset == insuranceToken &&
                                e.sellOrder.assetPair.priceAsset == purchase.sellOrder.assetPair.priceAsset
         let correctPrice = e.price == purchase.price - insurancePrice && e.amount == purchase.amount
         let correctHeight = height > purchaseTxHeight + freezePeriod
 
         #убеждаемся, что в транзакции-пруфе указан верный ID текущей транзакции
         let correctProof = extract(getBinary(this, toBase58String(purchase.id))) == e.sellOrder.id
         correctSender && correctAssetPair && correctPrice && correctHeight && correctProof
     case _ => false
   }
 case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}

Ví dụ: mã thông báo bảo hiểm có thể được coi là tài sản thông minh để cấm chuyển giao cho bên thứ ba.

Kế hoạch này cũng có thể được triển khai đối với các mã thông báo huy động vốn từ cộng đồng, được trả lại cho chủ sở hữu nếu số tiền yêu cầu chưa được thu thập.

Thuế giao dịch

Hợp đồng thông minh cũng được áp dụng trong trường hợp cần thu thuế đối với mỗi giao dịch với một số loại tài sản. Điều này có thể được thực hiện thông qua một nội dung mới được cài đặt sự tài trợ đối với các giao dịch với tài sản thông minh:

1. Chúng tôi phát hành FeeCoin, số tiền này sẽ được gửi tới người dùng với mức giá cố định: 0,01 WAVES = 0,001 FeeCoin.

2. Đặt tài trợ cho FeeCoin và tỷ giá hối đoái: 0,001 WAVES = 0,001 FeeCoin.

3. Đặt tập lệnh sau cho tài sản thông minh:

let feeAssetId = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
let taxDivisor = 10
 
match tx {
  case t: TransferTransaction =>
    t.feeAssetId == feeAssetId && t.fee == t.amount / taxDivisor
  case e: ExchangeTransaction | MassTransferTransaction => false
  case _ => true
}

Giờ đây, mỗi khi ai đó chuyển N tài sản thông minh, họ sẽ cung cấp cho bạn FeeCoin với số lượng N/taxDivisor (có thể mua từ bạn với giá 10 *N/taxDivisor WAVES) và bạn sẽ cung cấp cho người khai thác N/taxDivisor WAVES. Kết quả là lợi nhuận (thuế) của bạn sẽ là 9*N / taxDivisor WAVES.

Bạn cũng có thể thực hiện đánh thuế bằng cách sử dụng tập lệnh tài sản thông minh và MassTransferTransaction:

let taxDivisor = 10
 
match tx {
  case t : MassTransferTransaction =>
    let twoTransfers = size(t.transfers) == 2
    let issuerIsRecipient = t.transfers[0].recipient == addressFromString("3MgkTXzD72BTfYpd9UW42wdqTVg8HqnXEfc")
    let taxesPaid = t.transfers[0].amount >= t.transfers[1].amount / taxDivisor
    twoTransfers && issuerIsRecipient && taxesPaid
  case _ => false
}

Chương trình hoàn tiền và khách hàng thân thiết

Hoàn tiền là một loại chương trình khách hàng thân thiết trong đó người mua nhận lại một phần số tiền đã chi cho sản phẩm hoặc dịch vụ.

Khi thực hiện trường hợp này bằng tài khoản thông minh, chúng ta phải kiểm tra bằng chứng tương tự như trường hợp bảo hiểm. Để ngăn chi tiêu gấp đôi, người dùng phải gửi DataTransaction với (key, value) = (purchaseTransactionId, cashbackTransactionId) trước khi nhận được tiền hoàn lại.

Chúng tôi cũng phải đặt lệnh cấm đối với các khóa hiện có bằng DataTransaction. cashbackDivisor - đơn vị chia cho phần hoàn lại tiền. Những thứ kia. nếu tỷ lệ hoàn lại tiền là 0.1 thì Số chia hoàn lại 1 / 0.1 = 10.

let cashbackToken = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
 
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let cashbackDivisor = 10
match tx {
 
 #убеждаемся, что, если поступила дата-транзакция, то у нее ровно одно поле и в стейте еще нет такого ключа
 case d : DataTransaction => size(d.data) == 1 && !isDefined(getBinary(this, d.data[0].key))
 case e : TransferTransaction =>
 
   #если у транзакции нет седьмого пруфа, проверяем корректность подписи
   if !isDefined(e.proofs[7]) then
     sigVerify(e.bodyBytes, e.proofs[0], e.senderPublicKey)
   else
 
     #если у транзакции есть седьмой пруф, извлекаем из него транзакцию и узнаём её высоту
     let purchaseTx = transactionById(e.proofs[7])
     let purchaseTxHeight = extract(transactionHeightById(e.proofs[7]))
    
     #обрабатываем транзакцию из пруфа
     match purchaseTx {
       case purchase : TransferTransaction =>
         let correctSender = purchase.sender == e.sender
         let correctAsset = e.assetId == cashbackToken
         let correctPrice = e.amount == purchase.amount / cashbackDivisor
 
         #убеждаемся, что в транзакции-пруфе указан верный ID текущей транзакции
         let correctProof = extract(getBinary(this, toBase58String(purchase.id))) == e.id
         correctSender && correctAsset && correctPrice && correctProof
     case _ => false
   }
 case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}

Hoán đổi nguyên tử

Hoán đổi nguyên tử cho phép người dùng trao đổi tài sản mà không cần sự trợ giúp của sàn giao dịch. Với hoán đổi nguyên tử, cả hai người tham gia giao dịch đều phải xác nhận nó trong một khoảng thời gian nhất định.

Nếu ít nhất một trong những người tham gia không cung cấp xác nhận chính xác về giao dịch trong thời gian quy định cho giao dịch thì giao dịch sẽ bị hủy và việc trao đổi không diễn ra.

Trong ví dụ của chúng tôi, chúng tôi sẽ sử dụng tập lệnh tài khoản thông minh sau:

let Bob = Address(base58'3NBVqYXrapgJP9atQccdBPAgJPwHDKkh6A8')
let Alice = Address(base58'3PNX6XwMeEXaaP1rf5MCk8weYeF7z2vJZBg')
 
let beforeHeight = 100000
 
let secret = base58'BN6RTYGWcwektQfSFzH8raYo9awaLgQ7pLyWLQY4S4F5'
match tx {
  case t: TransferTransaction =>
    let txToBob = t.recipient == Bob && sha256(t.proofs[0]) == secret && 20 + beforeHeight >= height
    let backToAliceAfterHeight = height >= 21 + beforeHeight && t.recipient == Alice
    txToBob || backToAliceAfterHeight
  case _ => false
}

Trong bài viết tiếp theo, chúng ta sẽ xem xét việc sử dụng tài khoản thông minh trong các công cụ tài chính như quyền chọn, hợp đồng tương lai và hóa đơn.

Nguồn: www.habr.com

Thêm một lời nhận xét