Waves スマート アカウントとスマート アセットの金融商品への応用

Waves スマート アカウントとスマート アセットの金融商品への応用

前に статье 私たちは、オークションやロイヤルティ プログラムなど、ビジネスでスマート アカウントを使用するいくつかの事例を検討しました。
今日は、スマート アカウントとスマート アセットがオプション、先物、手形などの金融商品の透明性と信頼性をどのように向上させることができるかについて説明します。

オプション

オプションは、買い手に特定の価格または特定の日付より前に資産を購入する権利を与える交換契約ですが、そうする義務はありません。

オプションの実装は次のようになります。

当社は、商品としてのオプション自体にはスマートアセットを使用し、取引所として機能してオプションを発行する参加者にはスマートアカウントを使用します。取引所参加者は、expirationStart ブロックの高さとexpirationEnd ブロックの高さの間の sellPrice 価格で、一定量の特定の資産を売却することを約束します。

スマートアセットコードでは、指定された高さの間でのみ取引されることをチェックするだけで、それ以外は何もチェックしません。ルールの遵守に関するすべての責任は取引所参加者のコードに委ねられます。

スマート資産コード:

let expirationStart = 100000
let expirationEnd = 101440

match tx {
   case some : ExchangeTransaction | TransferTransaction =>
       height > expirationStart && height <= expirationEnd
   case _ => false
}

アクションは次のように行われると仮定します。取引所参加者はオプションを販売して資産を購入し、他の参加者はこれらのオプションを転送または取引できます。購入する権利を行使するには、潜在的な買い手は希望する数のオプションを売り手、つまり取引所参加者の口座に送金する必要があります。次に、完了した送金に関する情報を交換参加者の州口座に書き込みます。その後、指定された売買条件に従って ExchangeTransaction を続行できるようになります。

スマート アカウント コードでは、売買の最終行為のために通過する ExchangeTransaction が指定された条件を満たしていること、および参加者が取引参加者のアカウントに送信したユニット数を正確に購入することを確認する必要があります。潜在的な購入者は、交換参加者が二重支出を回避できるように、転送に関する正しい DataTransaction を送信する必要があります。この DataTransaction では、買い手は自分のアドレスに等しいキーに従って、取引所参加者のアカウントに転送されたオプションの数、つまり購入できる資産単位の数に等しい値を入力します。

スマートアカウントコード:

#владелец аккаунта дает обязательство продать определенное количество юнитов ассета
#по цене sellPrice между высотами блоков expirationStart и expirationEnd

let expirationStart = 100000
let expirationEnd = 101440
let sellPrice = 10000
let amountAsset = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
let priceAsset = base58'9jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
#ID ассета-опциона
let optionsAsset = base58'7jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
#извлекаем из транзакции адрес отправителя
let this = tx.sender
match tx {
   case dataTx : DataTransaction =>
       #извлекаем количество юнитов из дата-транзакции по ключу (ID пользователя)
       let units = extract(getInteger(dataTx.data, dataTx.data[0].key))

       #извлекаем трансфер-транзакцию опционов из пруфа
       let e = transactionById(dataTx.proofs[2]) #
       match e {
           case transferTx : TransferTransaction =>
               #убеждаемся, что трансфер был на текущий адрес
               (transferTx.recipient == this) &&
               #убеждаемся, что отправитель транзакции написал в качестве ключа свой ID
               dataTx.data[0].key == toBase58String(transferTx.sender.bytes) &&
               sigVerify(dataTx.bodyBytes, dataTx.proofs[0], transferTx.senderPublicKey) &&
               #убеждаемся, что указанное количество юнитов соответствует посланному количеству опционов
               (units == transferTx.amount) &&
               #убеждаемся, что был переведен именно ассет-опцион
               (transferTx.assetId == optionsAsset)              
           case _ => false
       } &&
       size(dataTx.data) == 1 && !isDefined(getInteger(this, dataTx.data[0].key))
       && height > expirationStart && height <= expirationEnd
   case exchangeTx : ExchangeTransaction =>
       #убеждаемся, что итоговый обмен происходит по указанным заранее правилам
       let correctAssetPair = exchangeTx.sellOrder.assetPair.amountAsset == amountAsset &&
                                   exchangeTx.sellOrder.assetPair.priceAsset == priceAsset
       let correctPrice = exchangeTx.sellOrder.price == sellPrice
       #извлекаем дата-транзакцию из пруфа      
       let d = transactionById(exchangeTx.proofs[2])
       match d{
           case dataTx : DataTransaction =>
               let buyOrderSender = dataTx.data[0].key
               toBase58String(exchangeTx.buyOrder.sender.bytes) == buyOrderSender &&
               exchangeTx.amount == extract(getInteger(dataTx.data, buyOrderSender))
           case _ => false
       } &&
       exchangeTx.sellOrder.sender == this &&
       correctAssetPair && correctPrice &&
       height > expirationStart && height <= expirationEnd
   case _ => false
}

スマート口座の先物

オプションとは異なり、先物(先物契約)は権利ではなく、将来の特定の時点で契約によって固定された価格で資産を購入する買い手の義務です。

一般に、未来を書くことはオプションを書くことと似ています。ここで、スマート資産は未来として機能します。

また、買い手と売り手の両方が注文書に署名していることを確認する必要があります。未来とは、いかなる場合であっても果たさなければならない義務です。これは、売り手または参加者がその義務を拒否した場合、ネットワーク内のどの参加者もトランザクションを送信して、将来を実行できることを意味します。

スマート アセット スクリプトは、先物資産のすべての TransferTransaction と ExchangeTransaction を制御し、買い手参加者が取引所参加者からの先物資産の将来の購入の注文を作成した場合にのみ承認します。

この注文は有効であり、先物が発行される条件を満たしている必要があります。注文を検証するには、署名された注文のバイト表現とともにそのすべてのフィールドを購入者のアカウントの状態に入力し、外部検証を実行します。

現時点では、RIDE にはトランザクション バイトを解析するためのネイティブ関数は含まれていませんが、その実装に必要なツールはすべて含まれています。したがって、開発者はこの機能を自分で実装してみることができます。

マルチシグネチャアカウント / エスクロー

マルチ署名アカウントを使用すると、複数のユーザーが資産を共同管理できます (たとえば、資産の取引は 4 人中 3 人のユーザーが署名を持っている場合にのみ可能になります)。 RIDE 言語でマルチシグネチャ アカウントを作成するには、トランザクション証明を使用できます。

マルチシグネチャ口座は、契約当事者が義務を履行するまで資金を保持するエスクロー口座としても使用できます。

let alicePubKey  = base58'5AzfA9UfpWVYiwFwvdr77k6LWupSTGLb14b24oVdEpMM'
let bobPubKey    = base58'2KwU4vzdgPmKyf7q354H9kSyX9NZjNiq4qbnH2wi2VDF'
let cooperPubKey = base58'GbrUeGaBfmyFJjSQb9Z8uTCej5GzjXfRDVGJGrmgt5cD'
#выясняем, кто предоставил корректные подписи
let aliceSigned  = if(sigVerify(tx.bodyBytes, tx.proofs[0], alicePubKey)) then 1 else 0
let bobSigned    = if(sigVerify(tx.bodyBytes, tx.proofs[1], bobPubKey)) then 1 else 0
let cooperSigned = if(sigVerify(tx.bodyBytes, tx.proofs[2], cooperPubKey)) then 1 else 0
#суммируем все корректные подписи и проверяем их количество
aliceSigned + bobSigned + cooperSigned >= 2

トークン厳選レジストリ (TCR)

多くのブロックチェーン プラットフォームには有害な資産に関する問題があります。たとえば、手数料を支払ったアドレスであれば、Waves 上にアセットを作成できます。

ユーザーとブロックチェーン自体を有害な資産から保護するという問題は、トークン所有者によって生成されたトークン管理レジストリ (TCR) によって解決できます。

特定のトークンをリストに追加するために投票するには、所有者は発行されたトークンの総数の自分の取り分に等しい入札を行います。トークンは、保有者の過半数が賛成票を投じた場合に登録されます。

この例では、count の現在の値が 0 である場合にのみ、状態キー key =asset_name を使用して、ユーザーが検討のために (「チャレンジ」期間中) リストにトークンを追加できるようにします。

また、ユーザーはウォレットにこのトークンの残高がゼロ以外である必要があります。次に、投票期間が始まります。この期間中、ユーザーはウォレット内の各アセットに 1 回だけ投票でき、10 から XNUMX までの評価が与えられます。ユーザーの投票は、user_address+assetID という形式のキーで表されます。

let asset = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
let addingStartHeight = 1000
let votingStartHeight = 2000
let votingEndHeight = 3000

#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)

#извлекаем адрес из пруфа транзакции
let address = addressFromPublicKey(tx.proofs[1])
match tx {
   case t: DataTransaction =>
       if(height > addingStartHeight)
       then(
           if(height < votingStartHeight)
           then(
               #adding
               #выясняем, есть ли этот ассет у этого адреса
               let hasTokens = assetBalance(address, asset) > 0
               size(t.data) == 1
                     #убеждаемся, что этот ассет еще не был добавлен
                     && !isDefined(getInteger(this, toBase58String(asset)))
                     #убеждаемся, что по ключу-ассету добавляется значение равное 0
                     && extract(getInteger(t.data, toBase58String(asset))) == 0
                     && hasTokens
           )
           else(
               if(height < votingEndHeight)
               then
               (
                   #voting
                   #узнаем текущее количество голосов за данный ассет и задаваемое количество
                   let currentAmount = extract(getInteger(this, toBase58String(asset)))
                   let newAmount = extract(getInteger(t.data, toBase58String(asset)))
                   let betString = toBase58String(address.bytes) + toBase58String(asset)

                   #убеждаемся, что этот адрес еще не голосовал за этот ассет
                   let noBetBefore = !isDefined(getInteger(this, betString))
                   let isBetCorrect = extract(getInteger(t.data, betString)) > 0
                       && extract(getInteger(t.data, betString)) <= 10
                  
                   #убеждаемся, что у голосующего есть необходимые токены
                   let hasTokens = assetBalance(address, asset) > 0
                   #проверяем корректность значений транзакции
                   size(t.data) == 2 && isDefined(getInteger(this, toBase58String(asset)))
                       && newAmount == currentAmount + 1
                       && noBetBefore && isBetCorrect && hasTokens
               )
               else false
           ) && sigVerify(tx.bodyBytes, tx.proofs[0], tx.proofs[1])
       )
       else false
   case _ => false
}

購読料

この例では、スマート アカウントを使用して、指定された間隔で製品またはサービスの定期支払い (「サブスクリプション料金」) を行う方法を見ていきます。
ユーザーがスマート アカウントに (トランザクション証明を通じて) 必要な送金額を含む TransferTransaction ID を提供すると、アカウントの状態に {key: address, value:) を書き込むことができます。 true}.

これは、ユーザーが製品またはサービスへのサブスクリプションを確認することを意味します。サブスクリプションの有効期限が切れると、ネットワーク ユーザーは、状態内の対応するキーの反対側の値を設定できます。 false.

let subscriptionPeriod = 44000
let signature = tx.proofs[0]
let pk = tx.proofs[1]
let requiredAmount = 100000

#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
match tx {
   case d: DataTransaction =>
     #извлекаем дату последнего платежа
     let lastPaymentHeight = extract(getInteger(this, d.data[0].key + "_lastPayment"))
     size(d.data) == 1 && d.data[0].value == "false" && lastPaymentHeight + subscriptionPeriod < height
     ||
     (
       let address = d.data[0].key

       #извлекаем трансфер-транзакцию по ID, указанному в пруфах
       let ttx = transactionById(d.proofs[0])
       
       size(d.data) == 2
           && d.data[0].value == "true"
           && d.data[1].key == address + "_lastPayment"
           && match ttx {
             case purchase : TransferTransaction =>
               d.data[1].value == transactionHeightById(purchase.id)
               && toBase58String(purchase.sender.bytes) == address
               && purchase.amount == requiredAmount
               && purchase.recipient == this
               #убеждаемся, что ассет waves
               && !isDefined(purchase.assetId)
         case _ => false
       }
     )
   case _ => false
}

投票する

スマート アカウントを使用して、ブロックチェーン上で投票を実装できます。例としては、アンバサダー プログラム内で最も優れたアンバサダー レポートに投票することが挙げられます。アカウントの状態は、特定のオプションに対する投票を記録するためのプラットフォームとして使用されます。

この例では、特別な「投票」トークンを購入した人のみが投票を許可されます。参加者は、(key, value) = (purchaseTransactionId, buyTransactionId) のペアで DataTransaction を事前に送信します。このキーに別の値を設定することは禁止されています。住所と投票オプションを使用して、DataEntry を設定できるのは 1 回だけです。投票は指定期間内のみ可能です。

let asset = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
let address = addressFromPublicKey(tx.proofs[1])
let votingStartHeight = 2000
let votingEndHeight = 3000

#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
 match tx {
   case t: DataTransaction =>
     (height > votingStartHeight && height < votingEndHeight) &&
     #убеждаемся, что у транзакции правильная подпись
     sigVerify(tx.bodyBytes, tx.proofs[0], tx.proofs[1]) &&

     #проверяем, что пользователь отдает свой голос напротив своего адреса
     if (t.data[0].key == toBase58String(address.bytes))
     then (
       #извлекаем транзакцию перевод голосовательного токена из пруфов
       let purchaseTx = transactionById(t.proofs[7])
       
       match purchaseTx {
         case purchase : TransferTransaction =>
           let correctSender = purchase.sender == t.sender
           let correctAsset = purchase.assetId == asset
           let correctPrice = purchase.amount == 1
           let correctProof = extract(getBinary(this, toBase58String(purchase.id))) == t.id
           correctSender && correctAsset && correctPrice && correctProof
         case _ => false
       }
     )
     else
         size(t.data) == 1 && !isDefined(getBinary(this, t.data[0].key))
   case _ => false
}

両替

約束手形は、一方の当事者が要求に応じて、またはあらかじめ決められた期日に、他方の当事者に一定額を支払うことを要求する書面による義務です。

この例では、有効期限が請求書の支払い日に一致するスマート アカウントを使用します。

let expiration = 100000
let amount = 10
let asset =  base58'9jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
  
let Bob = Address(base58'3NBVqYXrapgJP9atQccdBPAgJPwHDKkh6A8')
let Alice = Address(base58'3PNX6XwMeEXaaP1rf5MCk8weYeF7z2vJZBg')
  
match tx {
    case t: TransferTransaction =>
        (t.assetId == asset)&&      
        (t.amount == amount)&&       
        (t.sender == Bob)&&
        (t.recipient == Alice)&&
        (sigVerify(t.bodyBytes, t.proofs[0], t.senderPublicKey))&&
        (height >= expiration)
    case _ => false
}

預金

預金とは、特定の条件(期間、金利)の下で銀行に資金を預けることです。
この例では、銀行の機能はスマート アカウントによって実行されます。デポジット期間に相当する一定のブロック数が経過すると、ユーザーは利息付きでお金を返すことができます。スクリプトは、ユーザーがアカウントからお金を引き出すことができるブロックの高さ (finalHeight) を指定します。

heightUnit - 時間の 10 単位内のブロック数 (月、年など)。まず、(key, value) = (initialTransferTransaction, futureDataTransaction) のペアを持つエントリを確認します。その後、ユーザーは、入金金額と入金期間中に発生した利息に関する正しい情報を指定して TransferTransaction を送信する必要があります。この情報は、現在の TransferTransaction プルーフに含まれる元の TransferTransaction と照合してチェックされます。デポジットディバイザーはデポジットシェアの逆数です (デポジットが 0,1% で受け入れられた場合、デポジットシェアは 1 で、デポジットデバイザー = 0,1/10 = XNUMX)。

let this = extract(tx.sender)
let depositDivisor = 10
let heightUnit = 1000
let finalHeight = 100000
 match tx {
   case e : TransferTransaction =>
     #извлекаем высоту транзакции по ID транзакции в седьмом пруфе  
     let depositHeight = extract(transactionHeightById(e.proofs[7]))

     #извлекаем транзакцию депозита
     let purchaseTx = transactionById(e.proofs[7])    
     match purchaseTx {
       case deposit : TransferTransaction =>
         let correctSender = deposit.sender == e.sender
         #убеждаемся, что пользователь переводит себе корректную сумму депозита + проценты
         let correctAmount = deposit.amount + deposit.amount / depositDivisor * (height - depositHeight) / heightUnit == e.amount
         let correctProof = extract(getBinary(this, toBase58String(deposit.id))) == e.id
         correctSender && correctProof && correctAmount
       case _ => false
     }
     && finalHeight <= height 
 case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}

このシリーズの 3 回目と最後の記事では、特定のアドレスに対するトランザクションの凍結や制限など、スマート アセットのさらなる使用法について見ていきます。

出所: habr.com

DDoS 保護機能を備えた信頼性の高いサイト用ホスティング、VPS VDS サーバーを購入する 🔥 DDoS攻撃対策付きの信頼性の高いウェブサイトホスティング、VPS/VDSサーバーを購入しましょう | ProHoster