區塊鏈通常只與加密貨幣聯繫在一起,但 DLT 技術的應用領域要廣泛得多。 區塊鏈最有前景的應用領域之一是自動執行的智慧合約,並且不需要簽訂合約的各方之間的信任。
RIDE-智能合約語言
Waves 為智能合約開發了一種特殊的語言—RIDE。 其完整文件位於
RIDE 合約是謂詞,傳回「true」或「false」作為輸出。 因此,交易要么被記錄在區塊鏈中,要么被拒絕。 智能合約充分確保指定條件的滿足。 目前無法從 RIDE 中的合約產生交易。
目前,Waves 智能合約有兩種類型:智慧帳戶和智慧資產。 智慧帳戶是一個普通用戶帳戶,但為其設定了一個腳本來控制所有交易。 智慧型帳戶腳本可能如下所示,例如:
match tx {
case t: TransferTransaction | MassTransferTransaction => false
case _ => true
}
tx 是一個正在處理的交易,只有當它不是轉帳交易時,我們才允許使用模式匹配機制。 RIDE 中的模式比對用於檢查交易類型。 所有現有帳戶都可以在智慧帳戶腳本中處理
該腳本還可以聲明變量,使用“if-then-else”結構和其他方法來全面檢查條件。 為了確保合約具有可證明的完整性和複雜性(成本),並且在合約執行開始之前易於預測,RIDE 不包含循環或跳躍語句。
Waves 帳戶的其他功能包括「狀態」的存在,即帳戶的狀態。 您可以使用資料交易(DataTransaction)將無限數量的對(鍵,值)寫入帳戶狀態。 然後可以透過 REST API 和直接在智能合約中處理此資訊。
每筆交易都可以包含一系列證明,可以在其中輸入參與者的簽名、所需交易的 ID 等。
透過 RIDE 工作
對於完整的週期,包括建立帳戶、在其上安裝智慧合約和發送交易,您還可以使用與 REST API 互動的程式庫(例如,C#、C、Java、JavaScript、Python、Rust、Elixir) 。 要開始使用 IDE,只需按一下「新建」按鈕。
使用智能合約的可能性很廣泛:從禁止某些地址(「黑名單」)的交易到複雜的 dApp。
現在讓我們來看看在商業中使用智慧合約的具體範例:進行拍賣、保險和建立忠誠度計劃時。
拍賣
成功拍賣的條件之一是透明度:參與者必須確信不可能操縱出價。 這要歸功於區塊鏈,所有參與者都可以獲得有關所有投注及其時間的不可變數據。
在Waves區塊鏈上,出價可以透過DataTransaction記錄在拍賣帳戶狀態。
您也可以使用區塊編號設定拍賣的開始和結束時間:Waves 區塊鏈中區塊產生的頻率大約等於 60 秒。
1. 英式升價拍賣
英式拍賣的參與者相互競爭出價。 每個新的賭注必須超過先前的賭注。 當沒有更多的投標者超過最後的出價時,拍賣結束。 在這種情況下,最高出價者必須提供規定的金額。
還有一個拍賣選項,賣方為拍品設定最低價格,最終價格必須超過該價格。 否則,該拍品仍無法售出。
在此範例中,我們正在使用專為拍賣建立的帳戶。 拍賣時長為3000個區塊,起始價格為0,001 WAVES。 參與者可以透過發送包含關鍵「價格」和出價價值的數據交易來出價。
新出價的價格必須高於該金鑰的當前價格,且參與者的帳戶中必須至少有 [new_bid + Commission] 代幣。 投標者的地址必須記錄在DataTransaction中的「sender」欄位中,且目前投標區塊高度必須在拍賣期間內。
如果在拍賣結束時,參與者設定了最高價格,他可以發送一個ExchangeTransaction,以指定的價格和貨幣對支付相應的手數。
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. 荷蘭式拍賣價格遞減
在荷蘭式拍賣中,拍賣品最初的報價高於買家願意支付的價格。 價格逐步下降,直到其中一位參與者同意以當前價格購買該批次為止。
在此範例中,我們使用與前一個範例相同的常數,以及 Delta 減小時的價格步長。 帳戶腳本檢查參與者是否確實是第一個下注的人。 否則,數據交易不會被區塊鏈接受。
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. 拍賣“全額”
「全付」是指所有參與者都支付出價的拍賣,無論誰贏得了拍品。 每位新參與者都支付出價,出價最高的參與者將贏得拍賣品。
在我們的範例中,每位拍賣參與者透過 DataTransaction 使用 (key, value)* = (「獲勝者」, 地址),(「價格」, 價格) 出價。 只有當該參與者已經擁有帶有其簽名的 TransferTransaction 並且其出價高於所有先前的出價時,此類 DataTransaction 才會被批准。 拍賣一直持續到達到 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)
}
保險/眾籌
讓我們考慮一種情況,您需要確保用戶的資產免受財務損失。 例如,用戶希望得到保證,如果代幣貶值,他將能夠拿回為這些代幣支付的全額金額,並願意支付合理金額的保險。
為了實現這一點,需要發行「保險代幣」。 然後,在保單持有人的帳戶上安裝一個腳本,僅允許執行那些符合特定條件的 ExchangeTransactions。
為了防止雙重支出,您需要請求使用者提前向投保人帳戶發送DataTransaction(key,value)=(purchaseTransactionId,sellOrderId),並禁止使用已經使用過的key發送DataTransaction。
因此,用戶的證明中必須包含購買保險代幣的交易ID。 貨幣對必須與購買交易中的貨幣對相同。 費用也必須等於購買時確定的費用減去保險價格。
據了解,隨後保險帳戶以不低於用戶購買時的價格向用戶購買保險代幣:保險帳戶創建一個 ExchangeTransaction,用戶簽署訂單(如果交易正確完成),保險帳戶簽署第二個訂單和整個交易並將其發送到區塊鏈。
如果沒有發生購買,用戶可以根據腳本中描述的規則建立一個ExchangeTransaction,並將交易發送到區塊鏈。 這樣用戶就可以退還購買保險代幣所花的錢。
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)
}
例如,保險代幣可以成為智慧資產,以禁止其轉讓給第三方。
該方案也可以用於眾籌代幣,如果未籌集到所需金額,則將眾籌代幣退還給所有者。
交易稅
智能合約也適用於需要對多種資產的每筆交易徵稅的情況。 這可以透過安裝了新資產來完成
1.我們發行FeeCoin,以固定價格寄給用戶:0,01 WAVES = 0,001 FeeCoin。
2. 設定贊助FeeCoin和匯率:0,001 WAVES = 0,001 FeeCoin。
3. 為智慧資產設定以下腳本:
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
}
現在,每當有人轉移N個智慧資產時,他們都會給你N/taxDivisor數量的FeeCoin(可以以10 *N/taxDivisor WAVES向你購買),你會給礦工N/taxDivisor WAVES。 因此,您的利潤(稅)將為 9*N /taxDivisor WAVES。
您也可以使用智慧資產腳本和 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
}
現金回饋和忠誠度計劃
現金返還是一種忠誠度計劃,買家可以返還購買產品或服務的部分金額。
在使用智慧帳戶實施本案例時,我們必須按照保險案例中相同的方式檢查證據。 為了防止雙重支出,使用者在收到現金回饋之前必須發送一個帶有 (key, value) = (purchaseTransactionId, cashbackTransactionId) 的 DataTransaction。
我們還必須使用 DataTransaction 對現有金鑰設定禁令。 cashbackDivisor - 單位除以現金回饋份額。 那些。 若現金回饋份額為 0.1,則 cashbackDivisor 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)
}
原子交換
原子交換允許用戶在沒有交易所幫助的情況下交換資產。 對於原子交換,交易雙方都需要在一定時間內進行確認。
如果至少有一個參與者沒有在指定的交易時間內提供正確的交易確認,則交易將被取消且交換不會發生。
在我們的範例中,我們將使用以下智慧帳戶腳本:
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
}
在下一篇文章中,我們將探討智慧帳戶在選擇權、期貨和票據等金融工具中的使用。
來源: www.habr.com