Waves Smart Assets: Black and White Lists, Interval Trading
In two previous articles, we talked about smart accounts and how they can be used to conduct auctions and create loyalty programs , and also help ensure the transparency of financial instruments .
Now we will consider smart assets and several cases of their application, including freezing assets and creating restrictions on transactions at specified addresses.
Waves Smart Assets allow users to overlay scripts on assets, following the same mechanics as with smart accounts. Each new transaction created using the smart asset will be confirmed first by the script, and only then by the blockchain.
It is worth noting the following differences between smart assets and smart accounts:
- It is not possible to verify proofs in the smart asset code (we talked about them in the first article ).
- In the smart account code, you can check ExchangeTransaction only if your account is a match account. Otherwise, only the order is checked. You cannot check the order directly in the smart asset code, you can check the ExchangeTransaction, and if necessary, extract the order from it.
- A smart asset, unlike a smart account, does not have a state, but we still have access to the account states from the script.
Smart assets greatly simplify the writing of contracts, making the implementation of many cases concise and elegant.
Freezing assets
To freeze assets to a certain height of the targetHeight block , you can simply set this value in the script of the following smart asset:
let targetHeight = 1500000
height >= targetHeight
height - функция языка, возращающая текущую высоту.
Condition of a specific matchmaker
To set a specific matchup as desired, you can specify its address as a sender in a smart asset script of the following form:
match tx {
case t : ExchangeTransaction =>
t.sender == addressFromString("3PJaDyprvekvPXPuAtxrapacuDJopgJRaU3")
case _ => true
}
“White list” of recipients
To allow sending tokens only to certain accounts - to create a “white list” of recipients - you can use a smart asset with the following scheme, which checks entry into the list:
match tx {
case t : TransferTransaction =>
let trustedRecipient1 = addressFromString("3P6ms9EotRX8JwSrebeTXYVnzpsGCrKWLv4")
let trustedRecipient2 = addressFromString("3PLZcCJyYQnfWfzhKXRA4rteCQC9J1ewf5K")
let trustedRecipient3 = addressFromString("3PHrS6VNPRtUD8MHkfkmELavL8JnGtSq5sx")
t.recipient == trustedRecipient1 || t.recipient == trustedRecipient2 || t.recipient == trustedRecipient3
case _ => false
}
For security and provable language consistency, the list does not contain an iterator implementation. Therefore, it is defined as a set of specific elements.
Blacklist of recipients
In the same way, to prohibit sending tokens to certain accounts, you can create a blacklist. In this case, the exact same smart asset is used, but with the address checked for absence on the black list:
match tx {
case t : TransferTransaction =>
let bannedRecipient1 = addressFromString("3P6ms9EotRX8JwSrebeTXYVnzpsGCrKWLv4")
let bannedRecipient2 = addressFromString("3PLZcCJyYQnfWfzhKXRA4rteCQC9J1ewf5K")
let bannedRecipient3 = addressFromString("3PHrS6VNPRtUD8MHkfkmELavL8JnGtSq5sx")
t.recipient != bannedRecipient1 && t.recipient != bannedRecipient2 && t.recipient != bannedRecipient3
case _ => false
}
Sending with permission of the issuer
Using a smart asset, you can also set the option to send a smart asset only with permission of the issuer (commitment / debt label ). The issuer expresses its consent by posting the transaction ID in the style of his account:
match tx {
case t : TransferTransaction =>
let issuer = extract(addressFromString("3P6ms9EotRX8JwSrebeTXYVnzpsGCrKWLv4"))
#убеждаемся, что в стейте эмитента содержится ID текущей транзакции
isDefined(getInteger(issuer, toBase58String(t.id)))
case _ => false
}
Exchange only for certain coins
Smart Asset allows permission to exchange it only for certain coins. For example, to allow exchange only for bitcoins, you can use the following code:
let BTCId = base58'8LQW8f7P5d5PZM7GtZEBgaqRPGSzS3DfPuiXrURJ4AJS'
match tx {
case t : ExchangeTransaction =>
t.sellOrder.assetPair.priceAsset == BTCId ||
t.sellOrder.assetPair.amountAsset == BTCId
case _ => true
}
Trading at the price of the oracle
In the smart asset script, you can set the permission to trade only at the price fixed in the reliable oracle's story. Here is an example of such a script:
let oracle = Address(base58'3PLNmokt22NrSiNvCLvwMUP84LCMJqbXwAD')
let assetId = toBase58String(base58'oWgJN6YGZFtZrV8BWQ1PGktZikgg7jzGmtm16Ktyvjd')
match tx {
#запрещаем передачу ассета
case t: TransferTransaction | MassTransferTransaction => false
case e: ExchangeTransaction =>
#убеждаемся, что торговля происходит по цене, заданной в стейте оракла для этого ассета
let correctPrice = e.price == extract(getInteger(oracle, assetId))
#убеждаемся, что торговля происходит в обмен на WAVES
let correctPriceAsset = !isDefined(e.sellOrder.assetPair.priceAsset)
correctPrice && correctPriceAsset
case _ => true
}
Here we are faced with a non-obvious point when checking the ID of the asset with which the trade is carried out. The fact is that if the asset ID is not defined, then we are talking about WAVES. In the script, we make sure that trading is paired with WAVES, in this way.
Fixed price increase
You can set a fixed price for a smart asset, which will incrementally increase in a predetermined proportion. Here is an example asset script, the price of which will increase by 5% every 1000 blocks:
let startPrice = 10
let startHeight = 1000
let interval = 1000
#на сколько процентов цена увеличивается за один шаг
let raise = 5
match tx {
case t: TransferTransaction | MassTransferTransaction => false
case e: ExchangeTransaction =>
e.price == startPrice + ((height - startHeight) / interval) * (100 + raise) / 100
&& !isDefined(e.sellOrder.assetPair.priceAsset)
case _ => true
}
Interval trading
Also, thanks to the script, the trading of smart assets can be limited to predetermined intervals. Here is an example of such a script:
let startHeight = 10000
let interval = 44000
let limit = 1500
match tx {
case t: TransferTransaction | MassTransferTransaction | ExchangeTransaction =>
(height - startHeight) % interval < limit
case _ => true
}
In the script, we make sure that no more than limit intervals have passed since the start of trading startHeight . The interval length is equal to the number of blocks specified in the interval field .