Verwenden von Waves Smart Accounts: Von Auktionen zu Bonusprogrammen

Bild

Blockchain wird oft nur mit Kryptowährungen assoziiert, aber der Umfang der DLT-Technologie ist viel breiter. Einer der vielversprechendsten Bereiche für die Verwendung der Blockchain ist ein intelligenter Vertrag, der automatisch ausgeführt wird und kein Vertrauen zwischen den Parteien erfordert, die ihn abgeschlossen haben.

RIDE - Sprache für intelligente Verträge

Waves hat eine spezielle Sprache für intelligente Verträge entwickelt - RIDE. Die vollständige Dokumentation finden Sie hier . Und hier - ein Artikel zu diesem Thema über Habré.

Der Vertrag auf RIDE ist ein Prädikat und gibt für die Ausgabe "wahr" oder "falsch" zurück. Dementsprechend wird eine Transaktion entweder in die Blockchain geschrieben oder abgelehnt. Ein intelligenter Vertrag garantiert die Erfüllung bestimmter Bedingungen. Die Generierung von Transaktionen aus einem Vertrag in RIDE ist derzeit nicht möglich.

Heutzutage gibt es zwei Arten von Waves-Smart-Verträgen: Smart-Accounts und Smart-Assets. Ein Smart-Konto ist ein reguläres Benutzerkonto, für das jedoch ein Skript festgelegt ist, das alle Transaktionen steuert. Ein Smart Account-Skript könnte folgendermaßen aussehen:

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

tx ist eine verarbeitete Transaktion, die wir nur dann verwenden können, wenn es sich nicht um eine Übertragungstransaktion handelt. Der RIDE-Mustervergleich wird verwendet, um den Transaktionstyp zu überprüfen. Im Smart Account-Skript können alle vorhandenen Transaktionstypen verarbeitet werden.

Außerdem können Variablen im Skript deklariert werden, "Wenn-Dann-Sonst" -Konstruktionen und andere Methoden zur vollständigen Überprüfung von Bedingungen können verwendet werden. Damit die Verträge nachweislich abgeschlossen sind und eine Komplexität (Kosten) aufweisen, die vor Vertragsbeginn leicht vorherzusagen ist, enthält RIDE keine Schleifen und Operatoren wie Jump.

Unter anderen Merkmalen von Waves-Konten ist das Vorhandensein eines „Status“, dh des Status des Kontos. Mit Datentransaktionen (DataTransaction) kann eine unendliche Anzahl von Paaren (Schlüssel, Wert) in den Kontostatus geschrieben werden. Darüber hinaus können diese Informationen sowohl über die REST-API als auch direkt im Smart-Vertrag verarbeitet werden.

Jede Transaktion kann eine Reihe von Proofs enthalten, in die Sie die Unterschrift des Teilnehmers, die ID der erforderlichen Transaktion usw. eingeben können.

Wenn Sie mit RIDE über die IDE arbeiten, können Sie die kompilierte Form des Vertrags anzeigen (falls kompiliert), neue Konten erstellen und Skripte dafür festlegen sowie Transaktionen über die Befehlszeile senden.

Für einen vollständigen Zyklus, einschließlich der Erstellung eines Kontos, der Installation eines intelligenten Vertrags und des Sendens von Transaktionen, können Sie die Bibliothek auch zur Interaktion mit der REST-API verwenden (z. B. C #, C, Java, JavaScript, Python, Rust, Elixir). Um mit der IDE zu arbeiten, klicken Sie einfach auf die Schaltfläche NEU.

Die Möglichkeiten zur Verwendung intelligenter Verträge sind vielfältig: vom Verbot von Transaktionen über bestimmte Adressen (die "schwarze Liste") bis hin zu komplexen dApps.

Schauen wir uns nun konkrete Beispiele für den Einsatz intelligenter Verträge in Unternehmen an: während Auktionen, Versicherungen und der Erstellung von Treueprogrammen.

Auktionen

Eine der Voraussetzungen für eine erfolgreiche Auktion ist Transparenz: Bieter müssen sicher sein, dass Gebote nicht manipuliert werden können. Dies kann dank der Blockchain erreicht werden, bei der allen Teilnehmern unveränderte Daten zu allen Wetten und der Zeitpunkt ihrer Abgabe zur Verfügung stehen.

In der Waves-Blockchain können Gebote über DataTransaction im Auktionskontostatus erfasst werden.

Sie können die Start- und Endzeit der Auktion auch mithilfe von Blocknummern festlegen: Die Häufigkeit der Blockgenerierung in der Waves-Blockchain beträgt ca. 60 Sekunden.

1. Englische Auktion mit steigendem Preis

Teilnehmer des englischen Auktionsgebots konkurrieren miteinander. Jede neue Wette muss die vorherige überschreiten. Die Auktion endet, wenn nicht mehr bereit ist, das letzte Gebot zu überschreiten. In diesem Fall muss der Höchstbietende den angegebenen Betrag angeben.

Es gibt auch eine Auktionsoption, bei der der Verkäufer den Mindestpreis für das Los festlegt und der Endpreis diesen überschreiten muss. Ansonsten bleibt das Los unverkauft.

In diesem Beispiel arbeiten wir mit einem Konto, das speziell für die Auktion erstellt wurde. Die Auktionsdauer beträgt 3000 Blöcke und der anfängliche Preis des Loses beträgt 0,001 WELLEN. Ein Teilnehmer kann eine Wette abschließen, indem er eine DataTransaction mit dem "Preis" -Schlüssel und dem Wert seines Gebots sendet. In den Transaktionsnachweisen müssen Sie den öffentlichen Schlüssel und die Unterschrift des Absenders hinzufügen.

Der Preis der neuen Wette sollte höher sein als der aktuelle Preis für diesen Schlüssel, und der Teilnehmer muss mindestens [new_state + Provision] -Token auf dem Konto haben. Die Adresse des Bieters muss in das Feld "Absender" in der DataTransaction eingegeben werden, und die aktuelle Höhe des Gebotsblocks muss innerhalb des Auktionszeitraums liegen.

Wenn der Bieter am Ende der Auktion den höchsten Preis festgelegt hat, kann er ExchangeTransaction senden, um das entsprechende Los zum angegebenen Preis- und Währungspaar zu zahlen.

 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 pk = d.proofs[1] let address = addressFromPublicKey(pk) let priceIsBigger = newPrice > currentPrice let fee = 700000 let hasMoney = wavesBalance(address) + fee >= newPrice let correctFields = size(d.data) == 2 && extract(getString(d.data, "sender")) == toBase58String(address.bytes) startHeight <= height && height <= finishHeight && priceIsBigger && hasMoney && correctFields && sigVerify(tx.bodyBytes, tx.proofs[0], tx.proofs[1]) case o : Order => #       let pk = o.proofs[1] let address = addressFromPublicKey(pk) let senderIsWinner = address == addressFromString(extract(getString(this, "sender"))) #,    ,    let correctAssetPair = o.assetPair.amountAsset == token && ! isDefined(o.assetPair.priceAsset) let correctAmount = o.amount == 1 let correctPrice = o.price == extract(getInteger(this, "price")) height > finishHeight && senderIsWinner && correctAssetPair && correctAmount && correctPrice && sigVerify(o.bodyBytes, o.proofs[0], o.proofs[1]) case _ => false } 

2. Niederländische Versteigerung fallender Preise

Bei einer niederländischen Auktion wird zunächst viel zu einem höheren Preis angeboten, als der Käufer bereit ist zu zahlen. Der Preis wird schrittweise reduziert, bis einer der Teilnehmer dem Kauf des Loses zum aktuellen Preis zustimmt.

In diesem Beispiel verwenden wir dieselben Konstanten wie im vorherigen Beispiel sowie den Preisschritt beim Verringern des Deltas. Das Kontoskript prüft, ob der Teilnehmer wirklich der erste ist, der setzt. In den Transaktionsnachweisen müssen Sie den öffentlichen Schlüssel und die Signatur des Absenders hinzufügen. Andernfalls wird DataTransaction von der Blockchain nicht akzeptiert.

 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")) #       let pk = d.proofs[1] let address = addressFromPublicKey(pk) let correctFields = extract(getString(d.data, "sender")) == toBase58String(address.bytes) && size(d.data) == 2 && newPrice == currentPrice #,         "sender" let noBetsBefore = !isDefined(getInteger(this, "sender")) let fee = 700000 let hasMoney = wavesBalance(address) - fee >= newPrice startHeight <= height && height <= finishHeight && noBetsBefore && hasMoney && correctFields && sigVerify(tx.bodyBytes, tx.proofs[0], tx.proofs[1]) case o : Order => #       let pk = o.proofs[1] let address = addressFromPublicKey(pk) #,           sender let senderIsWinner = address == addressFromString(extract(getString(this, "sender"))) #,  mount   ,   - - waves let correctAssetPair = o.assetPair.amountAsset == token && ! isDefined(o.assetPair.priceAsset) let correctAmount = o.amount == 1 let correctPrice = o.price == extract(getInteger(this, "price")) height > finishHeight && senderIsWinner && correctAssetPair && correctAmount && correctPrice && sigVerify(o.bodyBytes, o.proofs[0], o.proofs[1]) case _ => false } 

3. All-Pay-Auktion

"All-Pay" - eine Auktion, bei der alle Teilnehmer das Gebot zahlen, unabhängig davon, wer das Los gewinnt. Jeder neue Teilnehmer zahlt die Wette und der Teilnehmer, der die maximale Wette abgeschlossen hat, gewinnt das Los.

In unserem Beispiel gibt jeder Auktionsteilnehmer ein Gebot über eine DataTransaction mit (Schlüssel, Wert) * = ("Gewinner", Adresse), ("Preis", Preis) ab. Eine solche DataTransaction wird nur genehmigt, wenn für diesen Teilnehmer bereits eine TransferTransaction mit ihrer Signatur vorhanden ist und die Rate höher ist als bei allen vorherigen. Die Auktion wird bis zum Ende von Höhe fortgesetzt.

 let startHeight = 1000 let endHeight = 2000 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) } 

Versicherung / Crowdfunding

Stellen Sie sich eine Situation vor, in der Sie Benutzervermögen vor finanziellen Verlusten versichern müssen. Beispielsweise möchte der Benutzer eine Garantie erhalten, dass er bei einer Wertminderung des Tokens den für diese Token gezahlten Gesamtbetrag zurückgeben kann und bereit ist, einen angemessenen Versicherungsbetrag zu zahlen.

Um dies zu implementieren, müssen Sie "Versicherungstoken" ausstellen. Anschließend wird auf dem Konto des Versicherungsnehmers ein Skript installiert, mit dem Sie nur die ExchangeTransactions ausführen können, die bestimmte Bedingungen erfüllen.

Um doppelte Ausgaben zu vermeiden, müssen Sie den Benutzer auffordern, DataTransaction im Voraus mit (Schlüssel, Wert) = (purchaseTransactionId, sellOrderId) an das Konto des Versicherungsnehmers zu senden und das Senden von DataTransactions mit dem bereits verwendeten Schlüssel zu verhindern.

Daher müssen die Proofs des Benutzers die Transaktions-ID des Kaufs des Versicherungstokens enthalten. Das Währungspaar muss mit dem Kaufvorgang identisch sein. Die Kosten sollten auch den zum Zeitpunkt des Kaufs erfassten Kosten abzüglich des Versicherungspreises entsprechen.

Es versteht sich, dass das Versicherungskonto anschließend die Versicherungstoken vom Benutzer zu einem Preis einlöst, der nicht niedriger ist als der Preis, zu dem er sie gekauft hat: Das Versicherungskonto erstellt ExchangeTransaction, der Benutzer signiert die Bestellung (wenn die Transaktion korrekt abgeschlossen wurde), das Versicherungskonto signiert die zweite Bestellung und die gesamte Transaktion und sendet sie an die Blockchain .

Wenn der Kauf nicht erfolgt, kann der Benutzer eine Bestellung gemäß den im Skript beschriebenen Regeln erstellen und die Transaktion an die Blockchain senden. So kann der Benutzer das für den Kauf versicherter Token ausgegebene Geld zurückgeben.

 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 o : Order => #     ,    if !isDefined(o.proofs[7]) then sigVerify(o.bodyBytes, o.proofs[0], o.senderPublicKey) else #     ,         let purchaseTx = transactionById(o.proofs[7]) let purchaseTxHeight = extract(transactionHeightById(o.proofs[7])) #    match purchaseTx { case purchase : ExchangeTransaction => let correctSender = purchase.sender == o.sender let correctAssetPair = o.assetPair.amountAsset == insuranceToken && purchase.sellOrder.assetPair.amountAsset == insuranceToken && o.assetPair.priceAsset == purchase.sellOrder.assetPair.priceAsset let correctPrice = o.price == purchase.price - insurancePrice && o.amount == purchase.amount let correctHeight = height > purchaseTxHeight + freezePeriod #,   -   ID   let correctProof = extract(getBinary(this, toBase58String(purchase.id))) == o.id correctSender && correctAssetPair && correctPrice && correctHeight && correctProof case _ => false } case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) } 

Ein Versicherungstoken kann beispielsweise zu einem intelligenten Vermögenswert gemacht werden, um seine Übertragung an Dritte zu verhindern.

Dieses Schema kann auch für Crowdfunding-Token implementiert werden, die an die Eigentümer zurückgegeben werden, wenn der erforderliche Betrag nicht eingezogen wurde.

Transaktionssteuern

Intelligente Verträge gelten auch in Fällen, in denen Steuern aus jeder Transaktion mit verschiedenen Arten von Vermögenswerten erhoben werden müssen. Dies kann durch ein neues gesponsertes Asset für Transaktionen mit intelligenten Assets erfolgen:

1. Wir veröffentlichen FeeCoin, das zu einem festen Preis an Benutzer gesendet wird: 0,01 WAVES = 0,001 FeeCoin.

2. Wir haben Sponsoring für FeeCoin und Wechselkurs eingerichtet: 0,001 WAVES = 0,001 FeeCoin.

3. Wir legen das folgende Skript für das Smart Asset fest:

 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 } 

Jedes Mal, wenn jemand N Smart Assets überträgt, gibt er Ihnen FeeCoin in Höhe von N / taxDivisor (der bei Ihnen für 10 * N / taxDivisor WAVES gekauft werden kann) und Sie geben dem Bergmann N / taxDivisor WAVES. Infolgedessen beträgt Ihr Gewinn (Steuer) 9 * N / taxDivisor WAVES.

Sie können auch mit einem Smart Asset-Skript und MassTransferTransaction Steuern erheben:

 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 } 

Cashback- und Treueprogramme

Cashback ist eine Art Treueprogramm, bei dem ein Teil des für ein Produkt oder eine Dienstleistung ausgegebenen Betrags an den Käufer zurückgegeben wird.

Wenn wir diesen Fall mit einem Smart Account implementieren, müssen wir die Beweise auf die gleiche Weise prüfen wie im Versicherungsfall. Um doppelte Ausgaben zu vermeiden, muss der Benutzer vor Erhalt eines Cashbacks eine DataTransaction mit (Schlüssel, Wert) = (purchaseTransactionId, cashbackTransactionId) senden.

Wir müssen auch vorhandene Schlüssel mithilfe einer DataTransaction sperren. CashbackDivisor - eine Einheit geteilt durch den Anteil des Cashbacks. Das heißt, Wenn der Cashback-Anteil 0,1 beträgt, ist 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) } 

Atomtausch

Mit Atomic Swap können Benutzer Assets ohne Hilfe eines Austauschs austauschen. Bei einem Atomic Swap müssen beide Teilnehmer der Transaktion diese innerhalb eines bestimmten Zeitraums bestätigen.

Wenn mindestens einer der Teilnehmer nicht innerhalb der für die Transaktion vorgesehenen Zeit die korrekte Bestätigung der Transaktion vorlegt, wird die Transaktion abgebrochen und es findet kein Umtausch statt.

In unserem Beispiel verwenden wir das folgende Smart Account-Skript:

 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 } 

Im nächsten Artikel werden wir die Verwendung intelligenter Konten in Finanzinstrumenten wie Optionen, Futures und Rechnungen betrachten.

Source: https://habr.com/ru/post/de442238/


All Articles