
În anteriorul Am analizat mai multe cazuri de utilizare a conturilor inteligente în afaceri, inclusiv licitații și programe de loialitate.
Astăzi vom vorbi despre modul în care conturile inteligente și activele inteligente pot îmbunătăți transparența și fiabilitatea instrumentelor financiare, cum ar fi opțiunile, contractele futures și facturile.
Opțiune
O opțiune este un contract de schimb care dă cumpărătorului dreptul de a cumpăra un activ la un anumit preț sau înainte de o anumită dată, dar nu îl obligă să facă acest lucru.
Implementarea opțiunii poate fi după cum urmează:
Folosim un activ inteligent pentru opțiunile în sine ca instrument și un cont inteligent pentru participantul care acționează ca un schimb și emite opțiuni. Participantul la schimb promite că va vinde o anumită cantitate dintr-un anumit activ la un preț de vânzare între înălțimile blocului expirationStart și expirationEnd).
În codul smart asset, pur și simplu vom verifica dacă este tranzacționat doar între înălțimile specificate și nu vom verifica nimic altceva; vom lăsa întreaga responsabilitate pentru respectarea regulilor codului participantului la schimb.
Codul bunului inteligent:
let expirationStart = 100000
let expirationEnd = 101440
match tx {
case some : ExchangeTransaction | TransferTransaction =>
height > expirationStart && height <= expirationEnd
case _ => false
}
Vom presupune că acțiunile au loc după cum urmează: un participant la bursă vinde opțiuni pentru a cumpăra un anumit activ, iar alți participanți pot transmite aceste opțiuni sau le pot tranzacționa. Pentru a-și exercita dreptul de cumpărare, potențialul cumpărător trebuie să transfere numărul dorit de opțiuni în contul vânzătorului, adică al participantului la schimb. Apoi, el scrie informații despre transferul finalizat în contul de stat al participantului la schimb și numai atunci ExchangeTransaction va putea continua în conformitate cu condițiile specificate de cumpărare și vânzare.
În codul de cont inteligent, trebuie să ne asigurăm că orice Tranzacție de schimb care trece prin acesta pentru actul final de cumpărare și vânzare îndeplinește condițiile specificate, iar participantul cumpără exact numărul de unități pe care le-a trimis în contul participantului la schimb. Potențialul cumpărător trebuie să trimită DataTransaction corectă despre transfer, astfel încât participantul la schimb să poată evita dublarea cheltuielilor. În această DataTransaction, cumpărătorul pune, conform cheii egale cu adresa sa, o valoare egală cu numărul de opțiuni transferate în contul participantului la schimb, adică numărul de unități de activ pe care acesta le poate cumpăra.
Cod de cont inteligent:
#владелец аккаунта дает обязательство продать определенное количество юнитов ассета
#по цене 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
}
Futuri pe conturi inteligente
Spre deosebire de o opțiune, un futures (contract futures) nu este un drept, ci o obligație a cumpărătorului de a cumpăra un activ la un preț stabilit prin contract la un anumit moment în viitor.
În general, scrierea unui viitor este similară cu scrierea unei opțiuni. Aici activul inteligent acționează ca un viitor.
De asemenea, trebuie să vă asigurați că atât cumpărătorul, cât și vânzătorul semnează comanda de cumpărare. Un viitor este o obligație care trebuie îndeplinită în orice caz. Aceasta înseamnă că dacă vânzătorul sau participantul își refuză obligațiile, orice participant în rețea poate trimite o tranzacție, executând astfel viitorul.
Scriptul de active inteligent controlează toate Tranzacțiile de transfer și Tranzacțiile de schimb ale activului futures, aprobându-le numai dacă participantul cumpărător a creat o comandă pentru o achiziție viitoare a activului futures de la participantul la schimb.
Acest ordin trebuie să fie valabil și să îndeplinească condițiile în care sunt emise futures. Pentru a valida o comandă, puteți introduce toate câmpurile acesteia în starea contului cumpărătorului împreună cu reprezentarea în octeți a comenzii semnate și apoi efectuați validarea externă.
Momentan, RIDE nu conține o funcție nativă pentru analiza octeților de tranzacție, dar include toate instrumentele necesare implementării acesteia. Prin urmare, dezvoltatorii pot încerca să implementeze ei înșiși această caracteristică.
Cont cu semnături multiple / escrow
Un cont cu semnături multiple permite mai multor utilizatori să gestioneze în comun activele (de exemplu, tranzacțiile cu active pot fi posibile numai dacă trei din patru utilizatori au semnături). Pentru a crea conturi cu semnături multiple în limbajul RIDE, putem folosi dovezi ale tranzacțiilor.
Un cont cu semnături multiple poate fi folosit și ca cont escrow în care fondurile sunt păstrate până când părțile la contract își îndeplinesc obligațiile.
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
Registrul curatat cu simboluri (TCR)
Multe platforme blockchain au o problemă cu activele toxice. De exemplu, orice adresă care a plătit un comision poate crea un activ pe Waves.
Problema protejării utilizatorilor și a blockchain-ului în sine de activele toxice poate fi rezolvată printr-un registru curat de token (TCR) generat de deținătorii de token-uri.
Pentru a vota pentru ca un anumit jeton să fie adăugat pe listă, deținătorul face o ofertă egală cu cota sa din numărul total de jetonuri emise. Un jeton este inclus în registru dacă majoritatea deținătorilor săi votează pentru el.
În exemplul nostru, permitem utilizatorului să adauge un token în listă pentru a fi luat în considerare (în perioada „provocarii”) folosind cheia de stat = asset_name, numai dacă valoarea curentă a numărului = 0.
De asemenea, utilizatorul trebuie să aibă un sold diferit de zero al acestui token în portofel. Urmează apoi perioada de vot, în care utilizatorul poate vota pentru fiecare activ din portofelul său, dar o singură dată, acordând un rating de la 1 la 10. Voturile utilizatorilor sunt reprezentate de chei de forma 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
}
Taxa de abonare
În acest exemplu, ne vom uita la utilizarea conturilor inteligente pentru a efectua plăți regulate pentru un produs sau serviciu la intervale specificate - „taxe de abonament”.
Dacă utilizatorul furnizează contului inteligent (prin dovezi ale tranzacției) un ID TransferTransaction cu suma necesară de fonduri transferate, el poate scrie {key: address, value:) în starea contului adevărat}.
Aceasta va însemna că utilizatorul își confirmă abonamentul la produs sau serviciu. Când abonamentul expiră, orice utilizator de rețea poate seta valoarea vizavi de cheia corespunzătoare din stat fals.
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
}
vot
Conturile inteligente pot fi folosite pentru a implementa votul pe blockchain. Un exemplu ar fi votarea pentru cel mai bun raport de ambasador din cadrul programului de ambasador. Starea contului este folosită ca platformă pentru înregistrarea voturilor pentru o anumită opțiune.
În acest exemplu, doar cei care au achiziționat jetoane speciale de „vot” au voie să voteze. Participantul trimite o DataTransaction în avans cu perechea (cheie, valoare) = (purchaseTransactionId, buyTransactionId). Setarea unei alte valori pentru această cheie este interzisă. Folosind adresa și opțiunea de vot, puteți seta DataEntry o singură dată. Votarea este posibilă numai în perioada specificată.
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
}
Bilet la ordin
Un bilet la ordin este o obligație scrisă care cere unei părți să plătească alteia o sumă fixă la cerere sau la o dată prestabilită.
În exemplul nostru, folosim un cont inteligent, a cărui dată de expirare corespunde datei de plată a facturii.
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
}
depozit
Depozitul este plasarea de fonduri într-o bancă în anumite condiții (termen, dobândă).
În exemplul nostru, funcția unei bănci este îndeplinită de un cont inteligent. După un anumit număr de blocuri, care corespunde perioadei de depunere, utilizatorul își poate returna banii cu dobândă. Scriptul specifică înălțimea blocului (finalHeight), după atingerea căreia utilizatorul poate retrage bani din cont.
heightUnit - numărul de blocuri într-o unitate de timp (de exemplu, lună, an etc.). Mai întâi verificăm o intrare cu perechea (cheie, valoare) = (initialTransferTransaction, futureDataTransaction). Utilizatorul trebuie apoi să trimită o Tranzacție de transfer cu informațiile corecte despre suma depozitului și dobânda acumulată pentru perioada de depozit. Aceste informații sunt verificate în comparație cu TransferTransaction originală, care este conținută în dovada actuală TransferTransaction. depositDivisor este numărul invers al cotei depozitului (dacă depozitul este acceptat la 10%, cota depozitului este 0,1, iar depositDevisor = 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)
}
În al treilea și ultimul articol din această serie, vom analiza mai multe utilizări ale activelor inteligente, inclusiv înghețarea și limitarea tranzacțiilor pentru anumite adrese.
Sursa: www.habr.com
