ブロックチェーンは暗号通貨のみに関連付けられることがよくありますが、DLT テクノロジーの適用分野ははるかに広いです。 ブロックチェーンの使用で最も有望な分野の XNUMX つは、自動的に実行され、締結した当事者間の信頼を必要としないスマート コントラクトです。
RIDE – スマートコントラクト用の言語
Waves は、スマート コントラクト用の特別な言語である RIDE を開発しました。 その完全なドキュメントは次の場所にあります
RIDE コントラクトは述語であり、出力として「true」または「false」を返します。 したがって、トランザクションはブロックチェーンに記録されるか、拒否されます。 スマート コントラクトは、指定された条件の履行を完全に保証します。 現在、RIDE でコントラクトからトランザクションを生成することはできません。
現在、Waves スマート コントラクトには、スマート アカウントとスマート アセットの XNUMX 種類があります。 スマート アカウントは通常のユーザー アカウントですが、すべてのトランザクションを制御するスクリプトが設定されています。 スマート アカウント スクリプトは次のようになります。
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 の使用を開始するには、[新規] ボタンをクリックするだけです。
スマート コントラクトを使用する可能性は多岐にわたります。特定のアドレスへのトランザクションの禁止 (「ブラック リスト」) から複雑な dApps までです。
次に、ビジネスにおけるスマート コントラクトの具体的な使用例を見てみましょう。オークション、保険、ロイヤルティ プログラムの作成時です。
オークション
オークションが成功するための条件の XNUMX つは透明性です。参加者は、入札を操作することは不可能であると確信する必要があります。 これはブロックチェーンのおかげで実現でき、すべての賭けと賭けが行われた時刻に関する不変のデータをすべての参加者が利用できるようになります。
Waves ブロックチェーンでは、DataTransaction を介してオークション アカウントの状態に入札を記録できます。
ブロック番号を使用してオークションの開始時刻と終了時刻を設定することもできます。Waves ブロックチェーンでのブロック生成の頻度は、 60 秒。
1. 英語昇順オークション
英国のオークションの参加者は、互いに競争して入札します。 新しい賭けはそれぞれ、前の賭けを超える必要があります。 最後の入札額を超える入札者がいなくなった時点で、オークションは終了します。 この場合、落札者様は記載の金額をご提示下さい。
売り手がロットの最低価格を設定し、最終価格がそれを超える必要があるオークション オプションもあります。 そうでない場合、ロットは売れ残ったままになります。
この例では、オークション用に特別に作成されたアカウントを使用しています。 オークション期間は 3000 ブロック、ロットの開始価格は 0,001 WAVES です。 参加者は、キー「価格」と入札額を含む DataTransaction を送信することで入札できます。
新しい入札の価格は、このキーの現在の価格よりも高くなければならず、参加者はアカウントに少なくとも [new_bid + Commission] トークンを持っている必要があります。 入札者のアドレスは DataTransaction の「送信者」フィールドに記録されている必要があり、現在の入札ブロックの高さはオークション期間内である必要があります。
オークションの終了時に参加者が最高価格を設定した場合、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. オランダのオークションで価格が下落
オランダのオークションでは、最初は買い手が支払ってもよい価格よりも高い価格で多くの商品が出品されます。 参加者の一人が現在の価格でロットを購入することに同意するまで、価格は段階的に引き下げられます。
この例では、前の例と同じ定数と、デルタが減少するときの価格ステップを使用します。 アカウント スクリプトは、参加者が本当に最初に賭けを行ったかどうかを確認します。 それ以外の場合、DataTransaction はブロックチェーンによって受け入れられません。
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 経由で入札します。 このような DataTransaction は、この参加者が署名付きの TransferTransaction をすでに持っており、入札額が以前のすべての入札額よりも高い場合にのみ承認されます。 オークションは 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)
}
保険・クラウドファンディング
ユーザーの資産を経済的損失から保護する必要がある状況を考えてみましょう。 たとえば、ユーザーは、トークンが下落した場合に、これらのトークンに支払った全額を取り戻すことができるという保証を望んでおり、適切な金額の保険を支払う意思があります。
これを実現するには「保険トークン」を発行する必要があります。 次に、保険契約者のアカウントにスクリプトがインストールされ、特定の条件を満たす ExchangeTransaction のみが実行されるようになります。
二重支払いを防ぐには、事前に(key, value) = (purchaseTransactionId, sellOrderId)で保険契約者の口座にDataTransactionを送信するようユーザーに要求し、既に使用されているキーを使用したDataTransactionの送信を禁止する必要があります。
したがって、ユーザーの証明には、保険トークン購入のトランザクション ID が含まれている必要があります。 通貨ペアは購入トランザクションと同じである必要があります。 また、コストは、購入時に固定されたコストから保険価格を差し引いたものでなければなりません。
その後、保険アカウントは、ユーザーが購入した価格よりも低くない価格でユーザーから保険トークンを購入することが理解されます。保険アカウントは ExchangeTransaction を作成し、ユーザーは注文に署名し (トランザクションが正しく完了した場合)、保険口座は XNUMX 番目の注文とトランザクション全体に署名し、ブロックチェーンに送信します。
購入が発生しない場合、ユーザーはスクリプトに記述されているルールに従って 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 / 税除数 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 の場合、キャッシュバック除数 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)
}
アトミックスワップ
アトミック スワップを使用すると、ユーザーは取引所の助けを借りずに資産を交換できます。 アトミック スワップでは、トランザクションの両方の参加者が一定期間内にそれを確認する必要があります。
参加者の少なくとも XNUMX 人がトランザクションに割り当てられた時間内にトランザクションの正しい確認を提供しなかった場合、トランザクションはキャンセルされ、交換は行われません。
この例では、次のスマート アカウント スクリプトを使用します。
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
}
次の記事では、オプション、先物、手形などの金融商品におけるスマート アカウントの使用について見ていきます。
出所: habr.com