Dans un article précédent , nous avons examiné plusieurs cas d'utilisation de comptes intelligents dans les entreprises, notamment des enchères et des programmes de fidélité.
Aujourd'hui, nous parlerons de la manière dont les comptes intelligents et les actifs intelligents peuvent accroître la transparence et la fiabilité des instruments financiers tels que les options, les contrats à terme et les factures.
OptionUne option est un contrat d'échange donnant à l'acheteur le droit d'acheter un actif à un certain prix ou avant une certaine date, mais ne l'obligeant pas à le faire.
L'exercice de l'option peut être le suivant:
Nous utilisons un actif intelligent pour les options elles-mêmes comme outil et un compte intelligent pour le participant qui agit comme échange et émet des options. Le participant à l'échange promet qu'il vendra une certaine quantité d'un certain actif au prix sellPrice entre les hauteurs des blocs expirationStart et expirationEnd).
Dans le code de l'actif intelligent, nous vérifions simplement qu'il n'est échangé qu'entre les hauteurs indiquées, et nous ne vérifierons rien d'autre, nous laisserons toute responsabilité d'observer les règles au code du participant à l'échange.
Code d'actif intelligent:let expirationStart = 100000 let expirationEnd = 101440 match tx { case some : ExchangeTransaction | TransferTransaction => height > expirationStart && height <= expirationEnd case _ => false }
Nous supposons que les actions sont les suivantes: le membre de l'échange vend des options pour l'achat de certains actifs, et le reste des participants peuvent transférer ces options ou les échanger. Pour exercer son droit d’achat, un acheteur potentiel doit transférer le nombre d’options souhaité sur le compte du vendeur, c’est-à-dire le participant à l’échange. Il note ensuite les informations sur le transfert terminé dans l'état du compte du membre d'échange et ce n'est qu'alors qu'ExchangeTransaction pourra passer par les conditions spécifiées d'achat et de vente.
Dans le code du compte intelligent, nous devons nous assurer que toute ExchangeTransaction qui le traverse pour l'acte d'achat-vente final remplit les conditions spécifiées, et le participant achète exactement le nombre d'unités qu'il a envoyé sur le compte du participant à l'échange. Un acheteur potentiel doit envoyer la DataTransaction correcte au sujet du transfert qui s'est produit, afin que le membre d'échange puisse éviter de doubler les dépenses. Dans cette DataTransaction, l'acheteur met sur une clé égale à son adresse une valeur égale au nombre d'options transférées sur le compte du participant à l'échange, c'est-à-dire le nombre d'unités d'actifs qu'il peut acheter.Code de compte intelligent: # # 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 = extract(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 order : Order => #, let correctAssetPair = order.assetPair.amountAsset == amountAsset && order.assetPair.priceAsset == priceAsset let correctPrice = order.price == sellPrice # - let d = transactionById(order.proofs[2]) match d{ case dataTx : DataTransaction => let buyOrderSender = dataTx.data[0].key toBase58String(order.sender.bytes) == buyOrderSender && order.amount == extract(getInteger(dataTx.data, buyOrderSender)) case _ => false } && order.sender == this && correctAssetPair && correctPrice && height > expirationStart && height <= expirationEnd case _ => false }
Futures de compte intelligentContrairement à une option, les futures (contrat à terme) ne sont pas un droit, mais l'obligation de l'acheteur d'acheter un actif à un prix fixe à un certain moment dans le futur.
En général, l'implémentation de futures est similaire à l'implémentation d'une option. Ici, un actif intelligent agit comme un futur.
Vous devez également vous assurer que l'acheteur et le vendeur signent le bon de commande. Le futur est une obligation qui doit être remplie dans tous les cas. Cela signifie que si un vendeur ou un participant refuse ses obligations, tout participant au réseau peut envoyer une transaction, exécutant ainsi des contrats à terme.
Le script Smart Asset contrôle tous les contrats à terme sur actifs TransferTransaction et ExchangeTransaction, les approuvant uniquement si le membre acheteur a créé une commande pour le futur achat d'actifs à terme auprès du membre Exchange.
Cet ordre doit être valide et satisfaire aux conditions d'émission des futures. Pour vérifier une commande, vous pouvez entrer tous ses champs dans l'état du compte de l'acheteur avec la représentation en octets de la commande signée, puis valider de l'extérieur.
Pour le moment, RIDE ne contient pas de fonction native pour analyser les octets de transaction, mais inclut tous les outils nécessaires à sa mise en œuvre. Par conséquent, les développeurs peuvent essayer d'implémenter cette fonctionnalité par eux-mêmes.
Compte multi-signé / engagementUn compte avec plusieurs signatures permet à plusieurs utilisateurs de gérer conjointement des actifs (par exemple, les transactions avec des actifs ne peuvent être possibles que si trois utilisateurs sur quatre ont des signatures). Pour créer des comptes avec plusieurs signatures dans le langage RIDE, nous pouvons utiliser des preuves de transaction.
Un compte avec une multi-signature peut également être utilisé pour un compte séquestre, dans lequel l'argent est stocké jusqu'à ce que les parties à l'accord remplissent leurs obligations.
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
Registre géré par jeton (TCR)Sur de nombreuses plateformes de blockchain, il y a un problème d'actifs toxiques. Par exemple, toute adresse ayant payé une commission peut créer un actif sur Waves.
Le registre organisé par jetons (TCR) généré par les détenteurs de jetons aide à résoudre le problème de la protection des utilisateurs et de la chaîne de blocs contre les actifs toxiques.
Pour voter pour l'ajout d'un jeton spécifique à la liste, le détenteur fait un pari égal à sa part de jetons du nombre total de jetons émis. Un jeton est inclus dans le registre si la plupart de ses détenteurs ont voté pour.
Dans notre exemple, nous autorisons l'utilisateur à ajouter le jeton à la liste pour examen (pendant la période de «challenge») par la clé de l'état key = nom_actif, uniquement si la valeur actuelle est count = 0.
De plus, l'utilisateur dans le portefeuille doit avoir un solde non nul de ce jeton. Vient ensuite la période de vote pendant laquelle l'utilisateur peut voter pour chaque actif de son portefeuille, mais une seule fois, donnant une note de 1 à 10. Les votes des utilisateurs sont représentés par des clés de la forme 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 }
Frais d'abonnementDans cet exemple, nous envisagerons l'utilisation de comptes intelligents pour effectuer des paiements réguliers pour un produit ou un service à des intervalles prédéterminés - des «frais mensuels».
Si un utilisateur fournit un compte intelligent (via des preuves de transaction) avec un ID TransferTransaction avec le montant requis des fonds transférés, il peut écrire {key: address, value:
true } dans l'état du compte.
Cela signifie que l'utilisateur confirme l'abonnement au produit ou au service. Lorsque l'abonnement expire, tout utilisateur du réseau peut définir la clé correspondante dans l'état sur
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 }
VoteLes comptes intelligents peuvent être utilisés pour implémenter le vote sur la blockchain. Un exemple serait un vote pour le meilleur rapport d'ambassadeur dans le cadre du programme des ambassadeurs. L'état du compte est utilisé comme plate-forme pour enregistrer les votes pour l'une ou l'autre option.
Dans cet exemple, le vote n'est autorisé qu'à ceux qui ont acheté des jetons «vote» spéciaux. Le participant envoie une DataTransaction à l'avance avec une paire de (clé, valeur) = (PurchaseTransactionId, buyTransactionId). Il est interdit de définir une valeur différente pour cette clé. En utilisant votre adresse et votre option de vote, vous ne pouvez installer DataEntry qu'une seule fois. Le vote n'est possible que pendant la période établie.
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 }
Lettre de changeUne lettre de change est une obligation écrite en vertu de laquelle une partie doit payer à l'autre un montant fixe au moment de la demande ou à une date prédéterminée.
Dans notre exemple, un compte intelligent est utilisé, dont la date d'expiration correspond à la date de paiement de la facture.
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 }
CautionDépôt - placement de fonds dans une banque à certaines conditions (durée, pourcentage).
Dans notre exemple, un compte intelligent remplit la fonction d'une banque. Après un certain nombre de blocs, ce qui correspond à la durée du dépôt, l'utilisateur peut restituer son argent avec un pourcentage. Le script définit la hauteur du bloc (finalHeight), après quoi l'utilisateur peut retirer de l'argent du compte.
heightUnit - le nombre de blocs dans une unité de temps (par exemple, mois, année, etc.). Tout d'abord, nous vérifions une entrée avec une paire (clé, valeur) = (initialTransferTransaction, futureDataTransaction). Ensuite, l'utilisateur doit envoyer à TransferTransaction les informations correctes sur le montant du dépôt et les intérêts courus pour la période du dépôt. Ces informations sont vérifiées par rapport à la TransferTransaction d'origine contenue dans la preuve de TransferTransaction actuelle. depositDivisor est le nombre inverse de la part du dépôt (si le dépôt est accepté à 10%, la part du dépôt est de 0,1 et depositDevisor = 1 / 0,1 = 10).
let depositDivisor = 10 let heightUnit = 1000 let finalHeight = 100000 let this = extract(tx.sender) 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) }
Dans le troisième et dernier article de cette série, nous examinerons plus d'options d'utilisation des actifs intelligents, y compris le gel et la restriction des transactions pour des adresses spécifiques.