Aplikace Waves chytrých účtů a chytrých aktiv ve finančních nástrojích

Aplikace Waves chytrých účtů a chytrých aktiv ve finančních nástrojích

V předchozím článek Podívali jsme se na několik případů používání chytrých účtů v podnikání, včetně aukcí a věrnostních programů.
Dnes si povíme o tom, jak mohou chytré účty a chytrá aktiva zlepšit transparentnost a spolehlivost finančních nástrojů, jako jsou opce, futures a směnky.

Volba

Opce je směnná smlouva, která dává kupujícímu právo koupit aktivum za určitou cenu nebo před určitým datem, ale nezavazuje ho k tomu.

Implementace možnosti může být následující:

Používáme inteligentní aktivum pro samotné opce jako nástroj a chytrý účet pro účastníka, který funguje jako burza a vydává opce. Účastník burzy slibuje, že prodá určité množství určitého aktiva za cenu sellPrice mezi výškami bloku expirationStart a expirationEnd).

V kódu chytrého aktiva jednoduše zkontrolujeme, že se obchoduje pouze mezi zadanými výškami, a nic dalšího už kontrolovat nebudeme, veškerou odpovědnost za dodržování pravidel přenecháme kódu účastníka burzy.

Kód chytrého aktiva:

let expirationStart = 100000
let expirationEnd = 101440

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

Budeme předpokládat, že akce probíhají následovně: účastník burzy prodává opce na nákup nějakého aktiva a ostatní účastníci mohou tyto opce přeposílat nebo s nimi obchodovat. Pro uplatnění svého práva na nákup musí potenciální kupující převést požadovaný počet opcí na účet prodávajícího, tedy účastníka burzy. Dále napíše informaci o provedeném převodu na státní účet účastníka burzy a teprve poté bude moci ExchangeTransaction pokračovat dle zadaných podmínek nákupu a prodeje.

V kódu chytrého účtu musíme dbát na to, aby jakákoliv ExchangeTransaction procházející přes něj pro závěrečný akt nákupu a prodeje splňovala zadané podmínky a účastník nakoupil přesně takový počet jednotek, který odeslal na účet účastníka burzy. Potenciální kupující musí odeslat správnou DataTransaction o převodu, aby se účastník burzy mohl vyhnout dvojí útratě. V této DataTransaction kupující vloží podle klíče rovného jeho adrese hodnotu rovnající se počtu opcí převedených na účet účastníka burzy, tedy počtu jednotek aktiv, které může koupit.

Kód chytrého účtu:

#владелец аккаунта дает обязательство продать определенное количество юнитов ассета
#по цене 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
}

Futures na chytré účty

Na rozdíl od opce není futures (futures kontrakt) právem, ale povinností kupujícího koupit aktivum za cenu stanovenou smlouvou v určitém okamžiku v budoucnosti.

Obecně platí, že sepsání futures je podobné sepsání opce. Chytré aktivum zde funguje jako budoucnost.

Musíte se také ujistit, že kupující i prodávající podepsali nákupní objednávku. Budoucnost je závazek, který musí být v každém případě splněn. To znamená, že pokud prodejce nebo účastník odmítne své závazky, může kterýkoli účastník sítě odeslat transakci, a tím realizovat budoucnost.

Skript inteligentního aktiva řídí všechny převodové transakce a burzovní transakce futures aktiva a schvaluje je pouze v případě, že kupující účastník vytvořil objednávku na budoucí nákup futures aktiva od účastníka burzy.

Tento příkaz musí být platný a splňovat podmínky, za kterých jsou futures emitovány. Pro ověření objednávky můžete zadat všechna její pole do stavu účtu kupujícího spolu s bajtovou reprezentací podepsané objednávky a poté provést externí ověření.

RIDE v tuto chvíli neobsahuje nativní funkci pro parsování transakčních bajtů, ale obsahuje všechny nástroje potřebné k její implementaci. Vývojáři se proto mohou pokusit tuto funkci implementovat sami.

Účet s více podpisy / úschova

Účet s více podpisy umožňuje více uživatelům společně spravovat aktiva (například transakce s aktivy mohou být možné pouze v případě, že tři ze čtyř uživatelů mají podpisy). Pro vytvoření účtů s více podpisy v jazyce RIDE můžeme použít transakční doklady.

Účet s více podpisy lze také použít jako vázaný účet, na kterém jsou uloženy finanční prostředky, dokud smluvní strany nesplní své závazky.

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

Registr spravovaný tokeny (TCR)

Mnoho blockchainových platforem má problém s toxickými aktivy. Například jakákoli adresa, která zaplatila provizi, může vytvořit aktivum na Waves.

Problém ochrany uživatelů a samotného blockchainu před toxickými aktivy lze vyřešit pomocí token curated registry (TCR) generovaného držiteli tokenů.

Pro hlasování pro konkrétní token, který má být přidán do seznamu, držitel zadá nabídku rovnající se jeho podílu na celkovém počtu vydaných tokenů. Token je zařazen do rejstříku, pokud pro něj hlasuje většina jeho držitelů.

V našem příkladu umožňujeme uživateli přidat token do seznamu ke zvážení (během období „výzvy“) pomocí klíče stavu key = asset_name, pouze pokud je aktuální hodnota count = 0.

Uživatel také musí mít v peněžence nenulový zůstatek tohoto tokenu. Poté přichází období hlasování, během kterého může uživatel hlasovat pro každé aktivum ve své peněžence, ale pouze jednou, a to s hodnocením od 1 do 10. Uživatelské hlasy jsou reprezentovány klíči ve tvaru 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
}

Poplatek za předplatné

V tomto příkladu se podíváme na použití chytrých účtů k provádění pravidelných plateb za produkt nebo službu ve stanovených intervalech – „poplatky za předplatné“.
Pokud uživatel poskytne chytrému účtu (prostřednictvím dokladů o transakci) ID TransferTransaction ID s požadovanou částkou převedených prostředků, může do stavu účtu napsat {klíč: adresa, hodnota:) pravdivý}.

To znamená, že uživatel potvrdí své předplatné produktu nebo služby. Po vypršení platnosti předplatného může kterýkoli uživatel sítě nastavit hodnotu naproti odpovídajícímu klíči ve stavu nepravdivý.

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
}

Hlasování

Chytré účty lze použít k implementaci hlasování na blockchainu. Příkladem může být hlasování o nejlepší ambasadorskou zprávu v rámci ambasadorského programu. Stav účtu se používá jako platforma pro zaznamenávání hlasů pro konkrétní možnost.

V tomto příkladu mohou hlasovat pouze ti, kteří si zakoupili speciální „hlasovací“ žetony. Účastník předem odešle DataTransaction s párem (klíč, hodnota) = (purchaseTransactionId, buyTransactionId). Nastavení jiné hodnoty pro tento klíč je zakázáno. Pomocí své adresy a možnosti hlasování můžete nastavit DataEntry pouze jednou. Hlasování je možné pouze ve stanoveném období.

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
}

Úpis

Směnka je písemný závazek, který vyžaduje, aby jedna strana zaplatila druhé straně pevnou částku na požádání nebo v předem stanovený den.

V našem příkladu používáme chytrý účet, jehož datum expirace odpovídá datu platby vyúčtování.

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
}

Vklad

Vklad je uložení peněžních prostředků v bance za určitých podmínek (termín, úrok).
V našem příkladu funkci banky plní chytrý účet. Po určitém počtu blokování, který odpovídá době vkladu, může uživatel vrátit své peníze i s úrokem. Skript určuje výšku bloku (finalHeight), po jejímž dosažení může uživatel vybrat peníze z účtu.

heightUnit - počet bloků za jednu časovou jednotku (například měsíc, rok atd.). Nejprve zkontrolujeme položku s párem (klíč, hodnota) = (počátečníTransferTransaction, futureDataTransaction). Uživatel pak musí odeslat převodovou transakci se správnými informacemi o výši vkladu a úrocích narostlých za dobu vkladu. Tyto informace jsou porovnány s původní převodní transakcí, která je obsažena v aktuálním dokladu převodové transakce. depositDivisor je převrácené číslo podílu na vkladu (pokud je vklad přijat ve výši 10 %, podíl na vkladu je 0,1 a vkladDevisor = 1/0,1 = 10).

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)
}

Ve třetím a posledním článku této série se podíváme na další využití chytrých aktiv, včetně zmrazení a omezení transakcí pro konkrétní adresy.

Zdroj: www.habr.com